puxadinho 0.0.0-stage → 0.1.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.
- package/README.md +68 -2
- package/bin/puxadinho.js +110 -0
- package/checksums.txt +4 -0
- package/package.json +28 -4
package/README.md
CHANGED
|
@@ -1,3 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# puxadinho (CLI)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Boxes Linux na nuvem para você e seus agentes.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx puxadinho@latest setup claude # ou: setup codex
|
|
7
|
+
npm i -g puxadinho # instala o comando "puxadinho"
|
|
8
|
+
puxadinho login
|
|
9
|
+
puxadinho create render-ep12
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Este pacote é só um lançador: no primeiro uso baixa o binário Go da sua plataforma
|
|
13
|
+
(`puxadinho_<os>_<arch>`, macOS e Linux, arm64 e amd64) da release `v<versão>`, confere o
|
|
14
|
+
sha256 contra o `checksums.txt` que vem dentro deste pacote, guarda em
|
|
15
|
+
`~/.cache/puxadinho/<versão>/` e executa com os mesmos argumentos e código de saída.
|
|
16
|
+
Sinais: Ctrl-C (SIGINT) chega direto ao binário pelo terminal e o lançador não o repete
|
|
17
|
+
(um segundo SIGINT faria a CLI sair sem limpar); SIGTERM e SIGHUP são repassados.
|
|
18
|
+
Requer Node 24 ou mais novo.
|
|
19
|
+
|
|
20
|
+
## Variáveis
|
|
21
|
+
|
|
22
|
+
- `PUXADINHO_DOWNLOAD_BASE`: base das releases (padrão
|
|
23
|
+
`https://github.com/hightech-company/puxadinho/releases/download`; aceita `file://`).
|
|
24
|
+
O binário é buscado em `<base>/v<versão>/`.
|
|
25
|
+
|
|
26
|
+
> TODO(release): o repositório acima é o `origin` atual do git; confirme o destino das
|
|
27
|
+
> releases antes de publicar (constante `DEFAULT_BASE` em `bin/puxadinho.js`).
|
|
28
|
+
|
|
29
|
+
## Códigos de saída
|
|
30
|
+
|
|
31
|
+
| Código | Significado |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| 0 | deu certo |
|
|
34
|
+
| 1 | falha geral (inclui `operation_failed`) |
|
|
35
|
+
| 2 | uso errado (flag, argumento; `invalid_argument`, `unknown_field`) |
|
|
36
|
+
| 3 | autenticação (`unauthorized`, `permission_denied`, `missing_scope`) |
|
|
37
|
+
| 4 | não encontrado (`not_found`) |
|
|
38
|
+
| 5 | limite ou plano (`spend_limit_reached`, `quota_exceeded`, `spec_not_allowed`, `box_cooldown`) |
|
|
39
|
+
| 6 | a box não ficou pronta a tempo (`create_timeout`) |
|
|
40
|
+
| 75 | falha passageira, tente de novo (qualquer erro `retryable`, inclui `timeout`) |
|
|
41
|
+
| 125 | falha do `puxadinho run` (infraestrutura, não o seu comando) |
|
|
42
|
+
|
|
43
|
+
`exec` e `run` saem com o código do próprio comando (sinal = 128+n, estouro de tempo = 124).
|
|
44
|
+
|
|
45
|
+
Dois códigos existem só na CLI (não vêm da API e não estão no contrato): `timeout` — a espera
|
|
46
|
+
local (`wait`, `jobs wait`, `--timeout`) acabou, mas a operação ou o job continua no servidor —
|
|
47
|
+
e `operation_failed` — uma operação terminou `failed` sem código de erro. Com `--json`, eles
|
|
48
|
+
aparecem no campo `code` como os demais.
|
|
49
|
+
|
|
50
|
+
## Segurança
|
|
51
|
+
|
|
52
|
+
- `puxadinho setup claude` usa `claude mcp add --header "Authorization: Bearer <chave>"`, que expõe
|
|
53
|
+
a chave por instantes na lista de processos da máquina. O Codex guarda a chave em
|
|
54
|
+
`~/.codex/config.toml` (modo 0600).
|
|
55
|
+
- O `checksums.txt` vem no pacote npm, não da origem do download: um binário trocado na release
|
|
56
|
+
(ou no meio do caminho) não confere e é descartado.
|
|
57
|
+
- A API padrão é `https://www.usepuxadinho.com.br` (troque com `PUXADINHO_API_URL` ou `--api-url`).
|
|
58
|
+
|
|
59
|
+
## Publicar uma versão
|
|
60
|
+
|
|
61
|
+
1. Ajuste `version` em `package.json`.
|
|
62
|
+
2. `pnpm --filter puxadinho build:binaries` gera `dist/v<versão>/puxadinho_*` e o `checksums.txt`
|
|
63
|
+
(na raiz do pacote, que vai para o npm, e uma cópia em `dist/v<versão>/`)
|
|
64
|
+
(`go build` com `-ldflags "-X puxadinho.app/go/internal/cli/api.Version=<versão>"`).
|
|
65
|
+
3. Crie a release `v<versão>` com os binários de `dist/v<versão>/`.
|
|
66
|
+
4. `pnpm --filter puxadinho publish` (o pacote leva o `checksums.txt` desses mesmos binários:
|
|
67
|
+
publique sempre logo depois do build da release).
|
|
68
|
+
|
|
69
|
+
`dist/` e o `checksums.txt` gerado não vão para o git; o `checksums.txt` vai para o npm.
|
package/bin/puxadinho.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Executa o binário da CLI desta máquina. Normalmente ele vem no pacote da plataforma
|
|
3
|
+
// (@puxadinho/cli-<os>-<cpu>, dependência opcional que o npm instala só na plataforma certa).
|
|
4
|
+
// Sem ele (ex.: npm com --no-optional), baixa uma vez, confere o sha256 contra o checksums.txt
|
|
5
|
+
// que vem DENTRO deste pacote (nunca o da origem do download) e o executa.
|
|
6
|
+
"use strict";
|
|
7
|
+
const { spawn } = require("node:child_process");
|
|
8
|
+
const { createHash } = require("node:crypto");
|
|
9
|
+
const fs = require("node:fs");
|
|
10
|
+
const os = require("node:os");
|
|
11
|
+
const path = require("node:path");
|
|
12
|
+
|
|
13
|
+
const pkg = require("../package.json");
|
|
14
|
+
const version = pkg.version;
|
|
15
|
+
// TODO(release): troque pelo repositório definitivo das releases (hoje: origin do git).
|
|
16
|
+
const DEFAULT_BASE = "https://github.com/hightech-company/puxadinho/releases/download";
|
|
17
|
+
|
|
18
|
+
const OS = { darwin: "darwin", linux: "linux" };
|
|
19
|
+
const ARCH = { arm64: "arm64", x64: "amd64" };
|
|
20
|
+
|
|
21
|
+
function fail(msg, code = 1) {
|
|
22
|
+
process.stderr.write(`puxadinho: ${msg}\n`);
|
|
23
|
+
process.exit(code);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function fetchBuffer(url) {
|
|
27
|
+
if (url.startsWith("file://")) {
|
|
28
|
+
return fs.readFileSync(new URL(url));
|
|
29
|
+
}
|
|
30
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
31
|
+
if (!res.ok) throw new Error(`${url} respondeu ${res.status}`);
|
|
32
|
+
return Buffer.from(await res.arrayBuffer());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Binário do pacote da plataforma (@puxadinho/cli-<os>-<cpu>), instalado pelo npm como dependência opcional. */
|
|
36
|
+
function platformBinary() {
|
|
37
|
+
try {
|
|
38
|
+
return require.resolve(`@puxadinho/cli-${process.platform}-${process.arch}/bin/puxadinho`);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function ensureBinary() {
|
|
45
|
+
const local = platformBinary();
|
|
46
|
+
if (local) return local;
|
|
47
|
+
const goos = OS[process.platform];
|
|
48
|
+
const goarch = ARCH[process.arch];
|
|
49
|
+
if (!goos || !goarch) {
|
|
50
|
+
fail(`plataforma sem suporte: ${process.platform}/${process.arch} (há binários para macOS e Linux, arm64 e x64).`);
|
|
51
|
+
}
|
|
52
|
+
const name = `puxadinho_${goos}_${goarch}`;
|
|
53
|
+
const cacheDir = path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), "puxadinho", version);
|
|
54
|
+
const target = path.join(cacheDir, name);
|
|
55
|
+
if (fs.existsSync(target)) return target;
|
|
56
|
+
|
|
57
|
+
// O sha256 esperado vem do pacote npm (gerado no build:binaries da mesma versão):
|
|
58
|
+
// uma origem de download comprometida não consegue trocar binário e soma juntos.
|
|
59
|
+
let sums;
|
|
60
|
+
try {
|
|
61
|
+
sums = fs.readFileSync(path.join(__dirname, "..", "checksums.txt"), "utf8");
|
|
62
|
+
} catch {
|
|
63
|
+
fail("este pacote veio sem checksums.txt; reinstale o puxadinho (npm i -g puxadinho@latest).");
|
|
64
|
+
}
|
|
65
|
+
const base = (process.env.PUXADINHO_DOWNLOAD_BASE || DEFAULT_BASE).replace(/\/+$/, "");
|
|
66
|
+
const root = `${base}/v${version}`;
|
|
67
|
+
process.stderr.write(`puxadinho: baixando ${name} v${version}…\n`);
|
|
68
|
+
let bin;
|
|
69
|
+
try {
|
|
70
|
+
bin = await fetchBuffer(`${root}/${name}`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
fail(`não consegui baixar o binário (${err.message}). Verifique a rede ou PUXADINHO_DOWNLOAD_BASE.`, 75);
|
|
73
|
+
}
|
|
74
|
+
const line = sums.split("\n").map((l) => l.trim().split(/\s+/)).find((p) => p[1] === name || p[1] === `*${name}`);
|
|
75
|
+
if (!line) fail(`o checksums.txt do pacote não lista ${name}.`);
|
|
76
|
+
const got = createHash("sha256").update(bin).digest("hex");
|
|
77
|
+
if (got !== line[0].toLowerCase()) fail(`sha256 de ${name} não confere; o download foi descartado.`);
|
|
78
|
+
|
|
79
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
80
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
81
|
+
fs.writeFileSync(tmp, bin, { mode: 0o755 });
|
|
82
|
+
fs.renameSync(tmp, target);
|
|
83
|
+
return target;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function main() {
|
|
87
|
+
const bin = await ensureBinary();
|
|
88
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
89
|
+
// Ctrl-C: o terminal já manda SIGINT ao grupo de processos inteiro (o filho
|
|
90
|
+
// recebe direto). Repassar daria um SEGUNDO SIGINT, que a CLI trata como
|
|
91
|
+
// "sair já" (o `run` deixaria a box para trás). Aqui só ignoramos o SIGINT e
|
|
92
|
+
// esperamos o filho sair. SIGTERM/SIGHUP (vindos de `kill`, ou do fim da
|
|
93
|
+
// sessão) chegam só a nós: esses são repassados.
|
|
94
|
+
process.on("SIGINT", () => {});
|
|
95
|
+
for (const sig of ["SIGTERM", "SIGHUP"]) {
|
|
96
|
+
process.on(sig, () => child.kill(sig));
|
|
97
|
+
}
|
|
98
|
+
child.on("error", (err) => fail(`não consegui executar ${bin}: ${err.message}`));
|
|
99
|
+
child.on("exit", (code, signal) => {
|
|
100
|
+
if (signal) {
|
|
101
|
+
// Morreu por sinal: repete o sinal em nós mesmos para o código de saída ser o mesmo.
|
|
102
|
+
process.removeAllListeners(signal);
|
|
103
|
+
process.kill(process.pid, signal);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
process.exit(code ?? 1);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main().catch((err) => fail(err.message));
|
package/checksums.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
95aabc79a3485810ff19ef30a63a34247c562bf0fba0087299d46f3996d56eef puxadinho_darwin_arm64
|
|
2
|
+
4b9555625a55de9bf7b69ff9fd2aafa182862abd36942e2cae15551aed74d637 puxadinho_darwin_amd64
|
|
3
|
+
85b066761a6c08d78e74b24ef41d0069520e77d7c84a69cd70f6eba987d86d78 puxadinho_linux_amd64
|
|
4
|
+
7ba1a5024a0ed7ff2ce518081b62f2edda59f3f663b15649b2bc74aac4e8c954 puxadinho_linux_arm64
|
package/package.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "puxadinho",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI do puxadinho: boxes Linux na nuvem para você e seus agentes.",
|
|
5
|
+
"license": "SEE LICENSE IN README.md",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"puxadinho": "bin/puxadinho.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"checksums.txt",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=24"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build:binaries": "node scripts/build-binaries.js"
|
|
23
|
+
},
|
|
24
|
+
"optionalDependencies": {
|
|
25
|
+
"@puxadinho/cli-darwin-arm64": "0.1.0",
|
|
26
|
+
"@puxadinho/cli-darwin-x64": "0.1.0",
|
|
27
|
+
"@puxadinho/cli-linux-arm64": "0.1.0",
|
|
28
|
+
"@puxadinho/cli-linux-x64": "0.1.0"
|
|
29
|
+
}
|
|
30
|
+
}
|