ps-claw 1.0.7 → 1.0.9

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.
Files changed (4) hide show
  1. package/README.md +225 -94
  2. package/cli.mjs +37 -58
  3. package/package.json +1 -2
  4. package/ps-claw.mjs +0 -661
package/README.md CHANGED
@@ -1,169 +1,300 @@
1
1
  # PS Claw 🦞
2
2
 
3
- **PS Claw** é uma versão leve do [OpenClaw](https://github.com/openclaw/openclaw) agente de IA autônomo multi-canal com interface web estilo ChatGPT.
3
+ **PS Claw** — Agente de IA autônomo leve com interface web estilo ChatGPT. Multi-canal (Telegram, Discord, WhatsApp) com suporte a modelos de múltiplos provedores (Claude, GPT-4, Gemini).
4
4
 
5
- Fork enxuto com foco em leveza e facilidade de uso, sem apps mobile, geração de mídia pesada ou componentes desnecessários.
5
+ Fork enxuto do [OpenClaw](https://github.com/openclaw/openclaw) focado em leveza, facilidade de uso e sem dependências pesadas.
6
6
 
7
7
  ---
8
8
 
9
- ## ⚡ Instalação rápida
9
+ ## ⚡ Início Rápido (5 minutos)
10
10
 
11
11
  ### Requisitos
12
- - [Node.js](https://nodejs.org/) v22.19 ou superior
12
+ - [Node.js](https://nodejs.org/) **v22.19+** (baixe a versão LTS)
13
+ - npm (incluído com Node.js)
13
14
 
14
- ### Instalar via npm (recomendado)
15
+ ### 1️⃣ Instalar e executar
15
16
 
16
17
  ```bash
17
- npm i ps-claw
18
+ npx ps-claw@latest web
18
19
  ```
19
20
 
20
- Pronto! O comando `ps-claw` fica disponível globalmente se instalado com `-g`:
21
+ Pronto! A interface web abre em **http://localhost:3000** 🎉
21
22
 
22
- ```bash
23
- npm i -g ps-claw
24
- ```
23
+ ---
25
24
 
26
- ### Instalar via npx (sem instalar)
25
+ ## 🌐 Interface Web
27
26
 
28
- ```bash
29
- npx ps-claw --help
30
- ```
27
+ ### O que é?
28
+ Interface visual estilo ChatGPT para conversar com o agente de IA. Salva histórico no navegador, permite trocar modelos, configurar gateways.
31
29
 
32
- ### Instalar a partir do código-fonte
30
+ ### Como usar?
33
31
 
34
32
  ```bash
35
- git clone https://github.com/Pedro21062014/Ps-Claw.git
36
- cd Ps-Claw
37
- pnpm install
38
- pnpm build
33
+ # Abrir a interface web
34
+ npx ps-claw web
35
+
36
+ # Ou executar tudo junto (agente + web)
37
+ npx ps-claw all
39
38
  ```
40
39
 
40
+ Acesse: **http://localhost:3000**
41
+
42
+ ### Configuração na interface
43
+
44
+ 1. Vá para a aba **🔌 Gateways**
45
+ - Clique **+ Adicionar**
46
+ - URL: `http://localhost:18789` (PS Claw local) ou qualquer API OpenAI-compatível
47
+ - Nome: "Meu Gateway"
48
+ - Clique **Adicionar**
49
+
50
+ 2. Vá para a aba **🤖 Modelos & Provedores**
51
+ - Adicione suas **chaves de API**:
52
+ - **Claude** (Anthropic): `sk-ant-...`
53
+ - **GPT-4** (OpenAI): `sk-...`
54
+ - **Gemini** (Google): `AIza...`
55
+ - **Mistral**: sua chave
56
+ - Selecione um modelo
57
+ - Clique no modelo para usá-lo
58
+
59
+ 3. Volte para **💬 Chat** e comece a conversar! 💬
60
+
41
61
  ---
42
62
 
43
- ## 🚀 Usando o PS Claw
63
+ ## 🔑 Chaves de API — Como obter
64
+
65
+ ### 🟠 Anthropic (Claude)
66
+
67
+ 1. Acesse https://console.anthropic.com
68
+ 2. Faça login ou crie conta
69
+ 3. Vá para **API Keys**
70
+ 4. Clique **Create Key**
71
+ 5. Copie a chave `sk-ant-...`
72
+ 6. Cole na aba **Modelos** do PS Claw
73
+
74
+ **Grátis?** Sim, Claude oferece créditos iniciais ($5-$20). Depois é por uso.
75
+
76
+ ### 🟢 OpenAI (GPT-4, GPT-4o)
77
+
78
+ 1. Acesse https://platform.openai.com
79
+ 2. Faça login ou crie conta
80
+ 3. Vá para **API Keys**
81
+ 4. Clique **Create new secret key**
82
+ 5. Copie a chave `sk-...`
83
+ 6. Cole na aba **Modelos** do PS Claw
84
+
85
+ **Grátis?** Sim, trial de $5-$18. Depois é por uso (mais barato que Claude).
86
+
87
+ ### 🔵 Google (Gemini)
88
+
89
+ 1. Acesse https://aistudio.google.com/apikey
90
+ 2. Clique **Create API Key**
91
+ 3. Selecione um projeto ou crie novo
92
+ 4. Copie a chave `AIza...`
93
+ 5. Cole na aba **Modelos** do PS Claw
44
94
 
45
- ### CLI Linha de comando
95
+ **Grátis?** Sim, 60 chamadas por minuto para sempre.
46
96
 
47
- Após instalar com `npm i -g ps-claw`:
97
+ ### 🟣 Mistral
98
+
99
+ 1. Acesse https://console.mistral.ai
100
+ 2. Faça login ou crie conta
101
+ 3. Vá para **API Keys**
102
+ 4. Clique **Generate a new key**
103
+ 5. Copie a chave
104
+ 6. Cole na aba **Modelos** do PS Claw
105
+
106
+ **Grátis?** Sim, trial de crédito. Depois por uso.
107
+
108
+ ---
109
+
110
+ ## 🚀 Alternativas de Uso
111
+
112
+ ### Via Git Clone (desenvolvedores)
48
113
 
49
114
  ```bash
50
- ps-claw --help # Mostra todos os comandos
51
- ps-claw --version # Versão instalada
52
- ps-claw web # Inicia a interface web
53
- ps-claw gateway run # Inicia o gateway
54
- ps-claw gateway status # Verifica status do gateway
55
- ps-claw doctor # Diagnóstico do sistema
56
- ps-claw models list # Lista modelos disponíveis
57
- ps-claw secrets set openai # Configura chave OpenAI
58
- ps-claw secrets set anthropic # Configura chave Anthropic
59
- ps-claw configure # Configuração interativa
115
+ git clone https://github.com/Pedro21062014/ps-claw-v2.git
116
+ cd ps-claw-v2
117
+ npm install
118
+ npx ps-claw web
60
119
  ```
61
120
 
62
- ### Interface Web
121
+ ### Instalar Globalmente
63
122
 
64
123
  ```bash
124
+ npm install -g ps-claw@latest
65
125
  ps-claw web
66
126
  ```
67
127
 
68
- Acesse **http://localhost:3000** no navegador.
128
+ Se não funcionar em Windows, use `npx ps-claw web` em vez disso.
69
129
 
70
- A interface web tem 4 abas:
71
- - 💬 **Chat** — conversas com histórico salvo localmente
72
- - 🔗 **Gateways** — conectar e gerenciar APIs (OpenAI, Anthropic, Ollama, LM Studio, etc.)
73
- - 🧠 **Modelos** — selecionar modelos por provedor (GPT-4o, Claude Opus 4.5, Gemini 2.5 Pro, DeepSeek...)
74
- - ⚙️ **Config** — temperatura, max tokens, system prompt, chaves de API, export/import de configurações
130
+ ### Atualizar
75
131
 
76
- ### Variáveis de ambiente
132
+ ```bash
133
+ npx ps-claw update
134
+ # ou
135
+ npm install -g ps-claw@latest
136
+ ```
137
+
138
+ ---
139
+
140
+ ## ⚙️ Configurações Avançadas
141
+
142
+ ### Variáveis de Ambiente
77
143
 
78
144
  ```bash
79
- PS_CLAW_WEB_PORT=3000 # Porta da interface web (padrão: 3000)
80
- PS_CLAW_GATEWAY_PORT=18789 # Porta do gateway (padrão: 18789)
81
- OPENCLAW_GATEWAY_TOKEN= # Token de autenticação do gateway
82
- OPENAI_API_KEY=sk-... # Chave OpenAI
83
- ANTHROPIC_API_KEY=sk-ant-... # Chave Anthropic
84
- GEMINI_API_KEY=... # Chave Google/Gemini
85
- DEEPSEEK_API_KEY=sk-... # Chave DeepSeek
86
- OPENROUTER_API_KEY=sk-or-... # Chave OpenRouter
145
+ # Porta da interface web (padrão: 3000)
146
+ set PS_CLAW_WEB_PORT=3000
147
+
148
+ # Porta do gateway PS Claw (padrão: 18789)
149
+ set PS_CLAW_GATEWAY_PORT=18789
150
+
151
+ # Token do gateway (se tiver autenticação)
152
+ set OPENCLAW_GATEWAY_TOKEN=seu_token_aqui
87
153
  ```
88
154
 
89
- Ou configure as chaves na aba **Config** da interface web.
155
+ ### Usar outro Gateway
156
+
157
+ Se você tem um PS Claw rodando em outro servidor:
158
+
159
+ 1. Na interface, aba **🔌 Gateways**
160
+ 2. Adicione a URL: `http://seu-servidor:18789`
161
+ 3. Pronto! Usa esse gateway
162
+
163
+ ### Usar API OpenAI-compatível
164
+
165
+ Muitos serviços são compatíveis com OpenAI API:
166
+
167
+ - **Ollama** (modelos locais): `http://localhost:11434`
168
+ - **Vllm** (inference server): `http://localhost:8000`
169
+ - **LiteLLM**: qualquer URL proxy
170
+
171
+ Configure na aba **Gateways** com a URL do seu servidor.
90
172
 
91
173
  ---
92
174
 
93
- ## 🔄 Atualizar o PS Claw
175
+ ## 📱 Canais (Telegram, Discord, WhatsApp)
176
+
177
+ Você pode conectar o PS Claw a:
178
+
179
+ - **Telegram** — Adicionar bot ao chat
180
+ - **Discord** — Adicionar bot ao servidor
181
+ - **WhatsApp** — Integração via Twilio ou Baileys
182
+ - **Slack** — Bot em workspace
183
+
184
+ Configure na interface ou edite `.env`:
94
185
 
95
186
  ```bash
96
- npm update -g ps-claw
187
+ # .env.example
188
+ TELEGRAM_BOT_TOKEN=seu_token
189
+ DISCORD_BOT_TOKEN=seu_token
190
+ WHATSAPP_PHONE=seu_numero
97
191
  ```
98
192
 
99
- Ou se instalou via código-fonte:
193
+ Copie `.env.example` para `.env` e preencha.
194
+
195
+ ---
196
+
197
+ ## 🐳 Docker
100
198
 
101
199
  ```bash
102
- cd Ps-Claw
103
- git pull
104
- pnpm install
105
- pnpm build
200
+ # Build
201
+ docker build -t ps-claw .
202
+
203
+ # Run
204
+ docker run -p 3000:3000 ps-claw web
106
205
  ```
107
206
 
108
207
  ---
109
208
 
110
- ## ✅ O que está incluído
209
+ ## ✅ Recursos Incluídos
111
210
 
112
211
  | Recurso | Status |
113
- |---|---|
114
- | Core do agente autônomo | ✅ |
115
- | Provedores: OpenAI, Anthropic, Google, DeepSeek, OpenRouter | ✅ |
116
- | Interface web com gateways, modelos e configurações | ✅ |
117
- | CLI com comandos completos | ✅ |
118
- | Pacote npm publicado | ✅ |
119
- | Busca na web (DuckDuckGo, Brave) | ✅ |
120
- | Suporte a MCP / Skills | ✅ |
212
+ |---------|--------|
213
+ | Interface web estilo ChatGPT | ✅ |
214
+ | Chat com histórico | ✅ |
215
+ | Múltiplos modelos | ✅ |
216
+ | Telegram, Discord, WhatsApp | ✅ |
217
+ | Busca na web | ✅ |
121
218
  | Memória persistente | ✅ |
122
- | Docker | ✅ |
123
- | Apps iOS / Android / macOS | removido |
124
- | Geração de vídeo / música / imagem | ❌ removido |
125
- | Transcrição em tempo real / TTS | ❌ removido |
219
+ | CLI + API | ✅ |
220
+ | Suporte MCP/Skills | |
221
+ | Apps iOS/Android | ❌ removido |
222
+ | Geração de vídeo/música | ❌ removido |
223
+ | Transcrição em tempo real | ❌ removido |
126
224
 
127
225
  ---
128
226
 
129
- ## 🐳 Docker
227
+ ## 🆘 Resolução de Problemas
228
+
229
+ ### "ps-claw: comando não encontrado"
230
+
231
+ **Solução:** Use `npx ps-claw web` em vez de `ps-claw web`
232
+
233
+ ### "localhost:3000 recusou conexão"
234
+
235
+ **Solução:** Verifique se algum programa já usa a porta 3000:
236
+
237
+ ```bash
238
+ # Windows
239
+ netstat -ano | findstr :3000
240
+
241
+ # Mac/Linux
242
+ lsof -i :3000
243
+ ```
244
+
245
+ Se estiver em uso, mude a porta:
130
246
 
131
247
  ```bash
132
- docker-compose up
248
+ set PS_CLAW_WEB_PORT=3001
249
+ npx ps-claw web
133
250
  ```
134
251
 
252
+ ### "Gateway offline"
253
+
254
+ **Solução:** Verifique se:
255
+
256
+ 1. A URL está correta (ex: `http://localhost:18789`)
257
+ 2. O gateway está rodando (se for local, execute `npx ps-claw start`)
258
+ 3. Firewall não está bloqueando
259
+
260
+ ### "Chave de API inválida"
261
+
262
+ **Solução:**
263
+
264
+ 1. Copie a chave completa (sem espaços)
265
+ 2. Verifique se é uma chave válida (não expirou)
266
+ 3. Cole de novo na aba **Modelos**
267
+
135
268
  ---
136
269
 
137
- ## 📁 Estrutura
270
+ ## 📚 Documentação Completa
138
271
 
139
- ```
140
- Ps-Claw/
141
- ├── ps-claw.mjs ← Binário CLI principal
142
- ├── scripts/
143
- │ └── build-all.mjs ← Script de build
144
- ├── web-ui/
145
- │ ├── server.mjs ← Servidor da interface web
146
- │ └── public/
147
- │ └── index.html ← Interface web (Chat, Gateways, Modelos, Config)
148
- ├── src/ ← Código-fonte TypeScript
149
- ├── extensions/ ← Provedores (OpenAI, Anthropic, DeepSeek...)
150
- ├── packages/ ← Bibliotecas internas
151
- ├── .env.example ← Exemplo de configuração
152
- └── package.json
153
- ```
272
+ - [OpenClaw (original)](https://github.com/openclaw/openclaw)
273
+ - [Anthropic Claude](https://console.anthropic.com/docs)
274
+ - [OpenAI API](https://platform.openai.com/docs)
275
+ - [Google Gemini](https://aistudio.google.com/app/apikey)
154
276
 
155
277
  ---
156
278
 
157
- ## 📦 npm
279
+ ## 📄 Licença
158
280
 
159
- Disponível em: [https://www.npmjs.com/package/ps-claw](https://www.npmjs.com/package/ps-claw)
281
+ MIT Baseado no [OpenClaw](https://github.com/openclaw/openclaw) por Peter Steinberger
160
282
 
161
- ```bash
162
- npm i ps-claw
163
- ```
283
+ ---
284
+
285
+ ## 🤝 Contribuições
286
+
287
+ Pull requests bem-vindo! Para mudanças grandes, abra uma issue primeiro.
164
288
 
165
289
  ---
166
290
 
167
- ## 📄 Licença
291
+ ## 💬 Suporte
292
+
293
+ - GitHub Issues: https://github.com/Pedro21062014/ps-claw-v2/issues
294
+ - Discussões: https://github.com/Pedro21062014/ps-claw-v2/discussions
295
+
296
+ ---
297
+
298
+ **Aproveite o PS Claw! 🦞**
168
299
 
169
- MIT baseado no [OpenClaw](https://github.com/openclaw/openclaw) por Peter Steinberger e contribuidores.
300
+ Dúvidas? Abra uma issue no GitHub ou entre em contato!
package/cli.mjs CHANGED
@@ -1,9 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * PS Claw CLI — ponto de entrada global
5
- * Uso: ps-claw [comando]
6
- * Comandos: start | web | all | update | help
4
+ * PS Claw CLI — standalone, sem dependência do dist/
7
5
  */
8
6
 
9
7
  import { spawn } from "node:child_process";
@@ -33,7 +31,7 @@ ${C.cyan}${C.bold} ██████╔╝███████╗ ██
33
31
  ${C.cyan}${C.bold} ██╔═══╝ ╚════██║ ██║ ██║ ██╔══██║██║███╗██║${C.reset}
34
32
  ${C.cyan}${C.bold} ██║ ███████║ ╚██████╗███████╗██║ ██║╚███╔███╔╝${C.reset}
35
33
  ${C.cyan}${C.bold} ╚═╝ ╚══════╝ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝${C.reset}
36
- ${C.dim}v1.0.0 — Lightweight AI Agent Gateway${C.reset}
34
+ ${C.dim}v1.0.8 — Lightweight AI Agent Gateway${C.reset}
37
35
  `);
38
36
  }
39
37
 
@@ -41,79 +39,60 @@ function help() {
41
39
  banner();
42
40
  console.log(` ${C.bold}Comandos:${C.reset}
43
41
 
44
- ${C.green}ps-claw start${C.reset} Inicia o agente PS Claw
45
- ${C.green}ps-claw web${C.reset} Inicia a interface web em http://localhost:3000
46
- ${C.green}ps-claw all${C.reset} Inicia o agente + interface web juntos
47
- ${C.green}ps-claw update${C.reset} Atualiza o PS Claw
48
- ${C.green}ps-claw help${C.reset} Esta mensagem
49
-
50
- ${C.bold}Início rápido:${C.reset}
51
-
52
- ${C.dim}# Via npm (global)${C.reset}
53
- npm install -g ps-claw
54
- ps-claw all
55
-
56
- ${C.dim}# Via git clone${C.reset}
57
- git clone https://github.com/Pedro21062014/ps-claw-v2.git
58
- cd ps-claw-v2 && npm install
59
- ps-claw all
42
+ ${C.green}npx ps-claw web${C.reset} Abre a interface web em http://localhost:3000
43
+ ${C.green}npx ps-claw start${C.reset} Inicia o agente PS Claw
44
+ ${C.green}npx ps-claw all${C.reset} Inicia tudo junto
45
+ ${C.green}npx ps-claw update${C.reset} Atualiza o PS Claw
46
+ ${C.green}npx ps-claw help${C.reset} Esta mensagem
60
47
 
61
48
  ${C.bold}Interface web:${C.reset} http://localhost:3000
62
49
  `);
63
50
  }
64
51
 
65
- function run(file, extraArgs = []) {
66
- if (!existsSync(file)) {
67
- console.error(`${C.red}❌ Arquivo não encontrado: ${file}${C.reset}`);
52
+ function startWeb() {
53
+ const srv = path.join(__dirname, "web-ui", "server.mjs");
54
+ if (!existsSync(srv)) {
55
+ console.error(`${C.red}❌ web-ui/server.mjs não encontrado!${C.reset}`);
68
56
  process.exit(1);
69
57
  }
70
- const proc = spawn(process.execPath, [file, ...extraArgs], { stdio: "inherit" });
58
+ console.log(`${C.green}🌐 Iniciando Interface Web...${C.reset}`);
59
+ console.log(`${C.cyan} Acesse: http://localhost:3000${C.reset}\n`);
60
+ const proc = spawn(process.execPath, [srv], { stdio: "inherit" });
71
61
  proc.on("exit", code => process.exit(code ?? 0));
72
- return proc;
73
62
  }
74
63
 
75
64
  function startAgent() {
76
- console.log(`${C.green}🦞 Iniciando PS Claw Agent...${C.reset}`);
77
- run(path.join(__dirname, "ps-claw.mjs"), args.slice(1));
78
- }
65
+ // Usa o ps-claw.mjs original do OpenClaw se existir e tiver dist/
66
+ // Caso contrário, avisa o usuário e abre a web
67
+ const distEntry = path.join(__dirname, "dist", "entry.mjs");
68
+ const distEntryJs = path.join(__dirname, "dist", "entry.js");
69
+
70
+ if (!existsSync(distEntry) && !existsSync(distEntryJs)) {
71
+ console.log(`${C.yellow}⚠️ O agente requer configuração adicional (dist/).${C.reset}`);
72
+ console.log(`${C.dim} Para usar a interface web, execute: npx ps-claw web${C.reset}\n`);
73
+ console.log(`${C.green}🌐 Iniciando Interface Web automaticamente...${C.reset}`);
74
+ console.log(`${C.cyan} Acesse: http://localhost:3000${C.reset}\n`);
75
+ startWeb();
76
+ return;
77
+ }
79
78
 
80
- function startWeb() {
81
- const srv = path.join(__dirname, "web-ui", "server.mjs");
82
- console.log(`${C.green}🌐 Interface Web http://localhost:3000${C.reset}`);
83
- run(srv);
79
+ console.log(`${C.green}🦞 Iniciando PS Claw Agent...${C.reset}`);
80
+ const proc = spawn(process.execPath, [path.join(__dirname, "ps-claw.mjs"), ...args.slice(1)], { stdio: "inherit" });
81
+ proc.on("exit", code => process.exit(code ?? 0));
84
82
  }
85
83
 
86
84
  function startAll() {
87
85
  banner();
88
- const agentFile = path.join(__dirname, "ps-claw.mjs");
89
- const webFile = path.join(__dirname, "web-ui", "server.mjs");
90
-
91
- console.log(`${C.green}🦞 Iniciando PS Claw Agent...${C.reset}`);
92
- const agent = spawn(process.execPath, [agentFile], { stdio: "inherit" });
93
-
94
- setTimeout(() => {
95
- if (existsSync(webFile)) {
96
- console.log(`\n${C.cyan}🌐 Iniciando Interface Web → http://localhost:3000${C.reset}\n`);
97
- const web = spawn(process.execPath, [webFile], { stdio: "inherit" });
98
- web.on("exit", code => process.exit(code ?? 0));
99
- }
100
- }, 1500);
101
-
102
- agent.on("exit", code => process.exit(code ?? 0));
103
- process.on("SIGINT", () => { agent.kill(); process.exit(0); });
86
+ startWeb();
104
87
  }
105
88
 
106
89
  function update() {
107
- banner();
108
- const script = path.join(__dirname, "update.sh");
109
- console.log(`${C.yellow}🔄 Verificando atualizações...${C.reset}\n`);
110
- if (!existsSync(script)) {
111
- console.log(`${C.yellow}Baixando versão mais recente...${C.reset}`);
112
- run("git", ["pull"]);
113
- return;
114
- }
115
- const proc = spawn("bash", [script], { stdio: "inherit" });
116
- proc.on("exit", code => process.exit(code ?? 0));
90
+ console.log(`${C.yellow}🔄 Atualizando PS Claw...${C.reset}\n`);
91
+ const proc = spawn("npm", ["install", "-g", "ps-claw@latest"], { stdio: "inherit", shell: true });
92
+ proc.on("exit", code => {
93
+ if (code === 0) console.log(`\n${C.green}✅ PS Claw atualizado!${C.reset}`);
94
+ process.exit(code ?? 0);
95
+ });
117
96
  }
118
97
 
119
98
  switch (cmd) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ps-claw",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "PS Claw - AI Agent Gateway with multi-provider support, web UI, and CLI. Lightweight fork of OpenClaw.",
5
5
  "keywords": [
6
6
  "ai",
@@ -41,7 +41,6 @@
41
41
  },
42
42
  "files": [
43
43
  "cli.mjs",
44
- "ps-claw.mjs",
45
44
  "web-ui/",
46
45
  "update.sh",
47
46
  "README.md",
package/ps-claw.mjs DELETED
@@ -1,661 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "node:child_process";
4
- import { existsSync, readFileSync, statSync } from "node:fs";
5
- import { access } from "node:fs/promises";
6
- import module from "node:module";
7
- import os from "node:os";
8
- import path from "node:path";
9
- import { fileURLToPath } from "node:url";
10
-
11
- const MIN_NODE_MAJOR = 22;
12
- const MIN_NODE_MINOR = 19;
13
- const MIN_NODE_VERSION = `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}`;
14
-
15
- const parseNodeVersion = (rawVersion) => {
16
- const [majorRaw = "0", minorRaw = "0"] = rawVersion.split(".");
17
- return {
18
- major: Number(majorRaw),
19
- minor: Number(minorRaw),
20
- };
21
- };
22
-
23
- const isSupportedNodeVersion = (version) =>
24
- version.major > MIN_NODE_MAJOR ||
25
- (version.major === MIN_NODE_MAJOR && version.minor >= MIN_NODE_MINOR);
26
-
27
- const ensureSupportedNodeVersion = () => {
28
- if (isSupportedNodeVersion(parseNodeVersion(process.versions.node))) {
29
- return;
30
- }
31
-
32
- process.stderr.write(
33
- `ps-claw: Node.js v${MIN_NODE_VERSION}+ is required (current: v${process.versions.node}).\n` +
34
- "If you use nvm, run:\n" +
35
- ` nvm install ${MIN_NODE_MAJOR}\n` +
36
- ` nvm use ${MIN_NODE_MAJOR}\n` +
37
- ` nvm alias default ${MIN_NODE_MAJOR}\n`,
38
- );
39
- process.exit(1);
40
- };
41
-
42
- ensureSupportedNodeVersion();
43
-
44
- if (tryOutputLauncherVersion(process.argv)) {
45
- process.exit(0);
46
- }
47
-
48
- const isSourceCheckoutLauncher = () =>
49
- existsSync(new URL("./.git", import.meta.url)) ||
50
- existsSync(new URL("./src/entry.ts", import.meta.url));
51
-
52
- const isNodeCompileCacheDisabled = () => process.env.NODE_DISABLE_COMPILE_CACHE !== undefined;
53
- const isNodeCompileCacheRequested = () =>
54
- Boolean(process.env.NODE_COMPILE_CACHE) && !isNodeCompileCacheDisabled();
55
- const sanitizeCompileCachePathSegment = (value) => {
56
- const normalized = value.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
57
- return normalized.length > 0 ? normalized : "unknown";
58
- };
59
- const readPackageVersion = () => {
60
- try {
61
- const parsed = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
62
- if (typeof parsed?.version === "string" && parsed.version.trim().length > 0) {
63
- return parsed.version;
64
- }
65
- } catch {
66
- // Fall through to an install-metadata-only cache key.
67
- }
68
- return "unknown";
69
- };
70
- const resolvePackagedCompileCacheDirectory = () => {
71
- const packageJsonUrl = new URL("./package.json", import.meta.url);
72
- const version = sanitizeCompileCachePathSegment(readPackageVersion());
73
- let installMarker = "no-package-json";
74
- try {
75
- const stat = statSync(packageJsonUrl);
76
- installMarker = `${Math.trunc(stat.mtimeMs)}-${stat.size}`;
77
- } catch {
78
- // Package archives should always have package.json, but keep startup best-effort.
79
- }
80
- const baseDirectory = isNodeCompileCacheRequested()
81
- ? process.env.NODE_COMPILE_CACHE
82
- : path.join(os.tmpdir(), "node-compile-cache");
83
- return path.join(
84
- baseDirectory,
85
- "ps-claw",
86
- version,
87
- sanitizeCompileCachePathSegment(installMarker),
88
- );
89
- };
90
-
91
- const respawnSignals =
92
- process.platform === "win32"
93
- ? ["SIGTERM", "SIGINT", "SIGBREAK"]
94
- : ["SIGTERM", "SIGINT", "SIGHUP", "SIGQUIT"];
95
- const respawnSignalExitGraceMs = 1_000;
96
- const respawnSignalForceKillGraceMs = 1_000;
97
- const respawnSignalHardExitGraceMs = 1_000;
98
-
99
- const runRespawnedChild = (command, args, env) => {
100
- const child = spawn(command, args, {
101
- stdio: "inherit",
102
- env,
103
- });
104
- const listeners = new Map();
105
- // This intentionally overlaps with src/entry.compile-cache.ts; keep the
106
- // respawn supervision behavior in sync until the launcher can share TS code.
107
- // Give the child a moment to honor forwarded signals, then exit the wrapper so
108
- // a child that ignores SIGTERM cannot keep the launcher alive indefinitely.
109
- let signalExitTimer = null;
110
- let signalForceKillTimer = null;
111
- let signalHardExitTimer = null;
112
- const detach = () => {
113
- for (const [signal, listener] of listeners) {
114
- process.off(signal, listener);
115
- }
116
- listeners.clear();
117
- if (signalExitTimer) {
118
- clearTimeout(signalExitTimer);
119
- signalExitTimer = null;
120
- }
121
- if (signalForceKillTimer) {
122
- clearTimeout(signalForceKillTimer);
123
- signalForceKillTimer = null;
124
- }
125
- if (signalHardExitTimer) {
126
- clearTimeout(signalHardExitTimer);
127
- signalHardExitTimer = null;
128
- }
129
- };
130
- const forceKillChild = () => {
131
- try {
132
- child.kill(process.platform === "win32" ? "SIGTERM" : "SIGKILL");
133
- } catch {
134
- // Best-effort shutdown fallback.
135
- }
136
- };
137
- const requestChildTermination = () => {
138
- try {
139
- child.kill("SIGTERM");
140
- } catch {
141
- // Best-effort shutdown fallback.
142
- }
143
- signalForceKillTimer = setTimeout(() => {
144
- forceKillChild();
145
- signalHardExitTimer = setTimeout(() => {
146
- process.exit(1);
147
- }, respawnSignalHardExitGraceMs);
148
- signalHardExitTimer.unref?.();
149
- }, respawnSignalForceKillGraceMs);
150
- signalForceKillTimer.unref?.();
151
- };
152
- const scheduleParentExit = () => {
153
- if (signalExitTimer) {
154
- return;
155
- }
156
- signalExitTimer = setTimeout(() => {
157
- requestChildTermination();
158
- }, respawnSignalExitGraceMs);
159
- signalExitTimer.unref?.();
160
- };
161
- for (const signal of respawnSignals) {
162
- const listener = () => {
163
- try {
164
- child.kill(signal);
165
- } catch {
166
- // Best-effort signal forwarding.
167
- }
168
- scheduleParentExit();
169
- };
170
- try {
171
- process.on(signal, listener);
172
- listeners.set(signal, listener);
173
- } catch {
174
- // Unsupported signal on this platform.
175
- }
176
- }
177
- child.once("exit", (code, signal) => {
178
- detach();
179
- if (signal) {
180
- process.exit(1);
181
- }
182
- process.exit(code ?? 1);
183
- });
184
- child.once("error", (error) => {
185
- detach();
186
- process.stderr.write(
187
- `[ps-claw] Failed to respawn launcher: ${
188
- error instanceof Error ? (error.stack ?? error.message) : String(error)
189
- }\n`,
190
- );
191
- process.exit(1);
192
- });
193
- return true;
194
- };
195
-
196
- const respawnWithoutCompileCacheIfNeeded = () => {
197
- if (!isSourceCheckoutLauncher()) {
198
- return false;
199
- }
200
- if (process.env.PS_CLAW_SOURCE_COMPILE_CACHE_RESPAWNED === "1") {
201
- return false;
202
- }
203
- if (!module.getCompileCacheDir?.() && !isNodeCompileCacheRequested()) {
204
- return false;
205
- }
206
- const env = {
207
- ...process.env,
208
- NODE_DISABLE_COMPILE_CACHE: "1",
209
- PS_CLAW_SOURCE_COMPILE_CACHE_RESPAWNED: "1",
210
- };
211
- delete env.NODE_COMPILE_CACHE;
212
- return runRespawnedChild(
213
- process.execPath,
214
- [...process.execArgv, fileURLToPath(import.meta.url), ...process.argv.slice(2)],
215
- env,
216
- );
217
- };
218
-
219
- const respawnWithPackagedCompileCacheIfNeeded = () => {
220
- if (isSourceCheckoutLauncher() || isNodeCompileCacheDisabled()) {
221
- return false;
222
- }
223
- if (process.env.PS_CLAW_PACKAGED_COMPILE_CACHE_RESPAWNED === "1") {
224
- return false;
225
- }
226
- const currentDirectory = module.getCompileCacheDir?.();
227
- if (!currentDirectory) {
228
- return false;
229
- }
230
- const desiredDirectory = resolvePackagedCompileCacheDirectory();
231
- if (path.resolve(currentDirectory) === path.resolve(desiredDirectory)) {
232
- return false;
233
- }
234
- const env = {
235
- ...process.env,
236
- NODE_COMPILE_CACHE: desiredDirectory,
237
- PS_CLAW_PACKAGED_COMPILE_CACHE_RESPAWNED: "1",
238
- };
239
- return runRespawnedChild(
240
- process.execPath,
241
- [...process.execArgv, fileURLToPath(import.meta.url), ...process.argv.slice(2)],
242
- env,
243
- );
244
- };
245
-
246
- const waitingForCompileCacheRespawn =
247
- respawnWithoutCompileCacheIfNeeded() || respawnWithPackagedCompileCacheIfNeeded();
248
-
249
- // https://nodejs.org/api/module.html#module-compile-cache
250
- if (
251
- !waitingForCompileCacheRespawn &&
252
- module.enableCompileCache &&
253
- !isNodeCompileCacheDisabled() &&
254
- !isSourceCheckoutLauncher()
255
- ) {
256
- try {
257
- module.enableCompileCache(resolvePackagedCompileCacheDirectory());
258
- } catch {
259
- // Ignore errors
260
- }
261
- }
262
-
263
- const getErrorMessage = (err) =>
264
- err && typeof err === "object" && "message" in err && typeof err.message === "string"
265
- ? err.message
266
- : "";
267
-
268
- const isModuleNotFoundError = (err) =>
269
- err && typeof err === "object" && "code" in err && err.code === "ERR_MODULE_NOT_FOUND";
270
-
271
- const isDirectModuleNotFoundError = (err, specifier) => {
272
- const message = getErrorMessage(err);
273
- const bunSpecifierMiss =
274
- message.includes(`Cannot find module '${specifier}'`) ||
275
- message.includes(`Cannot find module "${specifier}"`);
276
- const launcherPath = fileURLToPath(import.meta.url);
277
- const bunLauncherImporterMiss =
278
- message.includes(` from '${launcherPath}'`) || message.includes(` from "${launcherPath}"`);
279
-
280
- const expectedUrl = new URL(specifier, import.meta.url);
281
- const expectedPath = fileURLToPath(expectedUrl);
282
- const nodePathMiss =
283
- message.includes(`Cannot find module '${expectedPath}'`) ||
284
- message.includes(`Cannot find module "${expectedPath}"`);
285
-
286
- if (isModuleNotFoundError(err)) {
287
- if (err && typeof err === "object" && "url" in err && err.url === expectedUrl.href) {
288
- return true;
289
- }
290
- return nodePathMiss || (bunSpecifierMiss && bunLauncherImporterMiss);
291
- }
292
-
293
- return bunSpecifierMiss && bunLauncherImporterMiss;
294
- };
295
-
296
- const installProcessWarningFilter = async () => {
297
- // Keep bootstrap warnings consistent with the TypeScript runtime.
298
- for (const specifier of ["./dist/warning-filter.js", "./dist/warning-filter.mjs"]) {
299
- try {
300
- const mod = await import(specifier);
301
- if (typeof mod.installProcessWarningFilter === "function") {
302
- mod.installProcessWarningFilter();
303
- return;
304
- }
305
- } catch (err) {
306
- if (isDirectModuleNotFoundError(err, specifier)) {
307
- continue;
308
- }
309
- throw err;
310
- }
311
- }
312
- };
313
-
314
- const tryImport = async (specifier) => {
315
- try {
316
- await import(specifier);
317
- return true;
318
- } catch (err) {
319
- // Only swallow direct entry misses; rethrow transitive resolution failures.
320
- if (isDirectModuleNotFoundError(err, specifier)) {
321
- return false;
322
- }
323
- throw err;
324
- }
325
- };
326
-
327
- const exists = async (specifier) => {
328
- try {
329
- await access(new URL(specifier, import.meta.url));
330
- return true;
331
- } catch {
332
- return false;
333
- }
334
- };
335
-
336
- const buildMissingEntryErrorMessage = async () => {
337
- const lines = ["ps-claw: missing dist/entry.(m)js (build output)."];
338
- if (!(await exists("./src/entry.ts"))) {
339
- return lines.join("\n");
340
- }
341
-
342
- lines.push("This install looks like an unbuilt source tree or GitHub source archive.");
343
- lines.push(
344
- "Build locally with `pnpm install && pnpm build`, or install a built package instead.",
345
- );
346
- lines.push(
347
- "For pinned GitHub installs, use `npm install -g github:ps-claw/ps-claw#<ref>` instead of a raw `/archive/<ref>.tar.gz` URL.",
348
- );
349
- lines.push("For releases, use `npm install -g ps-claw@latest`.");
350
- return lines.join("\n");
351
- };
352
-
353
- const isBareRootHelpInvocation = (argv) =>
354
- argv.length === 3 && (argv[2] === "--help" || argv[2] === "-h");
355
-
356
- const resolvePrecomputedCommandHelp = (argv) => {
357
- if (argv.length !== 4 || (argv[3] !== "--help" && argv[3] !== "-h")) {
358
- return null;
359
- }
360
- if (argv[2] === "browser") {
361
- return { command: "browser", metadataKey: "browserHelpText" };
362
- }
363
- if (argv[2] === "secrets") {
364
- return { command: "secrets", metadataKey: "secretsHelpText" };
365
- }
366
- if (argv[2] === "nodes") {
367
- return { command: "nodes", metadataKey: "nodesHelpText" };
368
- }
369
- return null;
370
- };
371
-
372
- const isHelpFastPathDisabled = () =>
373
- process.env.PS_CLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH === "1";
374
-
375
- const normalizeLauncherHomeValue = (value) => {
376
- const trimmed = value?.trim();
377
- return trimmed && trimmed !== "undefined" && trimmed !== "null" ? trimmed : undefined;
378
- };
379
-
380
- const resolveLauncherOsHomeDir = () =>
381
- normalizeLauncherHomeValue(process.env.HOME) ??
382
- normalizeLauncherHomeValue(process.env.USERPROFILE) ??
383
- os.homedir();
384
-
385
- const resolveLauncherHomeDir = () => {
386
- const explicit = normalizeLauncherHomeValue(process.env.PS_CLAW_HOME);
387
- const rawHome =
388
- explicit && (explicit === "~" || explicit.startsWith("~/") || explicit.startsWith("~\\"))
389
- ? explicit.replace(/^~(?=$|[\\/])/, resolveLauncherOsHomeDir())
390
- : (explicit ?? resolveLauncherOsHomeDir());
391
- return path.resolve(rawHome);
392
- };
393
-
394
- const resolveLauncherUserPath = (input) => {
395
- if (input === "~") {
396
- return resolveLauncherHomeDir();
397
- }
398
- if (input.startsWith("~/") || input.startsWith("~\\")) {
399
- return path.join(resolveLauncherHomeDir(), input.slice(2));
400
- }
401
- return path.resolve(input);
402
- };
403
-
404
- const resolveLauncherConfigPaths = () => {
405
- const explicit = process.env.PS_CLAW_CONFIG_PATH?.trim();
406
- if (explicit) {
407
- return [resolveLauncherUserPath(explicit)];
408
- }
409
- const stateOverride = process.env.PS_CLAW_STATE_DIR?.trim();
410
- if (stateOverride) {
411
- const stateDir = resolveLauncherUserPath(stateOverride);
412
- return [path.join(stateDir, "ps-claw.json"), path.join(stateDir, "clawdbot.json")];
413
- }
414
- const homeDir = resolveLauncherHomeDir();
415
- return [
416
- path.join(homeDir, ".ps-claw", "ps-claw.json"),
417
- path.join(homeDir, ".ps-claw", "clawdbot.json"),
418
- path.join(homeDir, ".clawdbot", "ps-claw.json"),
419
- path.join(homeDir, ".clawdbot", "clawdbot.json"),
420
- ];
421
- };
422
-
423
- const shouldDeferRootHelpToRuntimeEntry = () => {
424
- if (
425
- process.env.PS_CLAW_BUNDLED_PLUGINS_DIR?.trim() ||
426
- process.env.PS_CLAW_DISABLE_BUNDLED_PLUGINS?.trim()
427
- ) {
428
- return true;
429
- }
430
- for (const configPath of resolveLauncherConfigPaths()) {
431
- try {
432
- const raw = readFileSync(configPath, "utf8");
433
- return /\bplugins\b|\$include\b/.test(raw);
434
- } catch {
435
- continue;
436
- }
437
- }
438
- return false;
439
- };
440
-
441
- const loadPrecomputedHelpText = (key) => {
442
- try {
443
- const raw = readFileSync(new URL("./dist/cli-startup-metadata.json", import.meta.url), "utf8");
444
- const parsed = JSON.parse(raw);
445
- const value = parsed?.[key];
446
- return typeof value === "string" && value.length > 0 ? value : null;
447
- } catch {
448
- return null;
449
- }
450
- };
451
-
452
- function tryOutputLauncherVersion(argv) {
453
- try {
454
- if (normalizeLauncherMetadataValue(process.env.PS_CLAW_CONTAINER)) {
455
- return false;
456
- }
457
- if (!isLauncherVersionFastPathArgv(argv)) {
458
- return false;
459
- }
460
- const version = resolveLauncherVersion();
461
- const commit = resolveLauncherCommit();
462
- process.stdout.write(commit ? `PS Claw ${version} (${commit})\n` : `PS Claw ${version}\n`);
463
- return true;
464
- } catch {
465
- return false;
466
- }
467
- }
468
-
469
- function isLauncherVersionFastPathArgv(argv) {
470
- return argv.length === 3 && (argv[2] === "--version" || argv[2] === "-V" || argv[2] === "-v");
471
- }
472
-
473
- function normalizeLauncherMetadataValue(value) {
474
- const trimmed = typeof value === "string" ? value.trim() : "";
475
- return trimmed && trimmed !== "undefined" && trimmed !== "null" ? trimmed : undefined;
476
- }
477
-
478
- function readLauncherJson(relativePath) {
479
- try {
480
- return JSON.parse(readFileSync(new URL(relativePath, import.meta.url), "utf8"));
481
- } catch {
482
- return null;
483
- }
484
- }
485
-
486
- function resolveLauncherVersion() {
487
- const packageJson = readLauncherJson("./package.json");
488
- const packageVersion = normalizeLauncherMetadataValue(packageJson?.version);
489
- if (packageVersion) {
490
- return packageVersion;
491
- }
492
- const buildInfo = readLauncherJson("./dist/build-info.json");
493
- const buildVersion = normalizeLauncherMetadataValue(buildInfo?.version);
494
- if (buildVersion) {
495
- return buildVersion;
496
- }
497
- return normalizeLauncherMetadataValue(process.env.PS_CLAW_BUNDLED_VERSION) ?? "0.0.0";
498
- }
499
-
500
- function resolveLauncherCommit() {
501
- const envCommit = formatLauncherCommit(process.env.GIT_COMMIT ?? process.env.GIT_SHA);
502
- if (envCommit) {
503
- return envCommit;
504
- }
505
- return (
506
- readLauncherGitCommit() ??
507
- formatLauncherCommit(readLauncherJson("./dist/build-info.json")?.commit) ??
508
- formatLauncherCommit(readLauncherJson("./package.json")?.gitHead) ??
509
- formatLauncherCommit(readLauncherJson("./package.json")?.githead)
510
- );
511
- }
512
-
513
- function formatLauncherCommit(value) {
514
- if (typeof value !== "string") {
515
- return null;
516
- }
517
- const match = value.trim().match(/[0-9a-fA-F]{7,40}/);
518
- return match ? match[0].slice(0, 7).toLowerCase() : null;
519
- }
520
-
521
- function readLauncherGitCommit() {
522
- try {
523
- const gitPath = fileURLToPath(new URL("./.git", import.meta.url));
524
- const headPath = resolveLauncherGitHeadPath(gitPath);
525
- if (!headPath) {
526
- return null;
527
- }
528
- const head = readFileSync(headPath, "utf8").trim();
529
- if (!head) {
530
- return null;
531
- }
532
- if (!head.startsWith("ref:")) {
533
- return formatLauncherCommit(head);
534
- }
535
- const ref = head.replace(/^ref:\s*/i, "").trim();
536
- if (!ref.startsWith("refs/") || path.isAbsolute(ref) || ref.split("/").includes("..")) {
537
- return null;
538
- }
539
- const refsBase = resolveLauncherGitRefsBase(headPath);
540
- const refPath = path.resolve(refsBase, ref);
541
- const rel = path.relative(refsBase, refPath);
542
- if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) {
543
- return null;
544
- }
545
- try {
546
- return formatLauncherCommit(readFileSync(refPath, "utf8"));
547
- } catch {
548
- return readLauncherPackedRef(refsBase, ref);
549
- }
550
- } catch {
551
- return null;
552
- }
553
- }
554
-
555
- function resolveLauncherGitHeadPath(gitPath) {
556
- try {
557
- if (statSync(gitPath).isDirectory()) {
558
- return path.join(gitPath, "HEAD");
559
- }
560
- const raw = readFileSync(gitPath, "utf8").trim();
561
- if (!raw.startsWith("gitdir:")) {
562
- return null;
563
- }
564
- return path.join(
565
- path.resolve(path.dirname(gitPath), raw.slice("gitdir:".length).trim()),
566
- "HEAD",
567
- );
568
- } catch {
569
- return null;
570
- }
571
- }
572
-
573
- function resolveLauncherGitRefsBase(headPath) {
574
- const gitDir = path.dirname(headPath);
575
- try {
576
- const commonDir = readFileSync(path.join(gitDir, "commondir"), "utf8").trim();
577
- return commonDir ? path.resolve(gitDir, commonDir) : gitDir;
578
- } catch {
579
- return gitDir;
580
- }
581
- }
582
-
583
- function readLauncherPackedRef(refsBase, ref) {
584
- try {
585
- const packedRefs = readFileSync(path.join(refsBase, "packed-refs"), "utf8");
586
- for (const line of packedRefs.split("\n")) {
587
- if (!line || line.startsWith("#") || line.startsWith("^")) {
588
- continue;
589
- }
590
- const [commit, packedRef] = line.trim().split(/\s+/, 2);
591
- if (packedRef === ref) {
592
- return formatLauncherCommit(commit);
593
- }
594
- }
595
- } catch {
596
- // fall through
597
- }
598
- return null;
599
- }
600
-
601
- const tryOutputBareRootHelp = async () => {
602
- if (!isBareRootHelpInvocation(process.argv)) {
603
- return false;
604
- }
605
- if (shouldDeferRootHelpToRuntimeEntry()) {
606
- return false;
607
- }
608
- const precomputed = loadPrecomputedHelpText("rootHelpText");
609
- if (precomputed) {
610
- process.stdout.write(precomputed);
611
- return true;
612
- }
613
- for (const specifier of ["./dist/cli/program/root-help.js", "./dist/cli/program/root-help.mjs"]) {
614
- try {
615
- const mod = await import(specifier);
616
- if (typeof mod.outputRootHelp === "function") {
617
- await mod.outputRootHelp();
618
- return true;
619
- }
620
- } catch (err) {
621
- if (isDirectModuleNotFoundError(err, specifier)) {
622
- continue;
623
- }
624
- throw err;
625
- }
626
- }
627
- return false;
628
- };
629
-
630
- const tryOutputPrecomputedCommandHelp = () => {
631
- const commandHelp = resolvePrecomputedCommandHelp(process.argv);
632
- if (!commandHelp) {
633
- return false;
634
- }
635
- if (commandHelp.command === "nodes" && shouldDeferRootHelpToRuntimeEntry()) {
636
- return false;
637
- }
638
- const precomputed = loadPrecomputedHelpText(commandHelp.metadataKey);
639
- if (!precomputed) {
640
- return false;
641
- }
642
- process.stdout.write(precomputed);
643
- return true;
644
- };
645
-
646
- if (!waitingForCompileCacheRespawn) {
647
- if (!isHelpFastPathDisabled() && (await tryOutputBareRootHelp())) {
648
- // OK
649
- } else if (!isHelpFastPathDisabled() && tryOutputPrecomputedCommandHelp()) {
650
- // OK
651
- } else {
652
- await installProcessWarningFilter();
653
- if (await tryImport("./dist/entry.js")) {
654
- // OK
655
- } else if (await tryImport("./dist/entry.mjs")) {
656
- // OK
657
- } else {
658
- throw new Error(await buildMissingEntryErrorMessage());
659
- }
660
- }
661
- }