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 CHANGED
@@ -1,3 +1,69 @@
1
- # Temporary Holding Version
1
+ # puxadinho (CLI)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.
@@ -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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }