@fixprompt/cli 0.0.1

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 (3) hide show
  1. package/README.md +86 -0
  2. package/dist/cli.js +238 -0
  3. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @fixprompt/cli
2
+
3
+ FixPrompt command-line tool. One subcommand for now: `deploy-start`, which posts a deploy marker to the broker so the dashboard knows what changed.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @fixprompt/cli
9
+ ```
10
+
11
+ Or just one-shot it with no install:
12
+
13
+ ```bash
14
+ npx @fixprompt/cli deploy-start --status success
15
+ ```
16
+
17
+ ## `fixprompt deploy-start`
18
+
19
+ Tells the broker that a deploy just happened. Reads your `commit_sha` / `branch` / `repo` / `commit_message` automatically when run inside a git working tree or under one of these CI providers:
20
+
21
+ - GitHub Actions (`GITHUB_SHA`, `GITHUB_REF_NAME`, `GITHUB_REPOSITORY`)
22
+ - Vercel (`VERCEL_GIT_COMMIT_SHA`, `VERCEL_GIT_COMMIT_REF`, `VERCEL_GIT_REPO_*`)
23
+ - Railway (`RAILWAY_GIT_COMMIT_SHA`, `RAILWAY_GIT_BRANCH`, …)
24
+
25
+ ### Auth
26
+
27
+ Pass via env vars (preferred for CI):
28
+
29
+ ```bash
30
+ export LOGHUB_SOURCE=my-app-prod # from project creation
31
+ export LOGHUB_KEY=k_... # from project creation / Rotate Key
32
+ ```
33
+
34
+ Or via flags:
35
+
36
+ ```bash
37
+ fixprompt deploy-start \
38
+ --source my-app-prod \
39
+ --key k_... \
40
+ --status success
41
+ ```
42
+
43
+ ### Flags
44
+
45
+ | flag | default | purpose |
46
+ |------|---------|---------|
47
+ | `--source <slug-prod>` | `$LOGHUB_SOURCE` / `$FIXPROMPT_SOURCE` | project source label |
48
+ | `--key <k_...>` | `$LOGHUB_KEY` / `$FIXPROMPT_KEY` | ingest key |
49
+ | `--endpoint <url>` | `$FIXPROMPT_ENDPOINT` or `https://geosloghub-production.up.railway.app` | broker URL |
50
+ | `--status <state>` | `in_progress` | `in_progress` \| `success` \| `failed` |
51
+ | `--commit-sha <sha>` | auto | override auto-detected commit |
52
+ | `--branch <name>` | auto | override auto-detected branch |
53
+ | `--message <text>` | auto | override auto-detected commit subject |
54
+
55
+ ### EAS / Expo
56
+
57
+ In `eas-build-post-install` or a custom workflow step:
58
+
59
+ ```bash
60
+ npx --yes @fixprompt/cli deploy-start --status success
61
+ ```
62
+
63
+ EAS env vars (`EAS_BUILD_GIT_COMMIT_HASH`, `EAS_BUILD_GIT_BRANCH_NAME`) aren't recognised yet — pass them explicitly until V2:
64
+
65
+ ```bash
66
+ npx --yes @fixprompt/cli deploy-start \
67
+ --status success \
68
+ --commit-sha "$EAS_BUILD_GIT_COMMIT_HASH" \
69
+ --branch "$EAS_BUILD_GIT_BRANCH_NAME"
70
+ ```
71
+
72
+ ### GitHub Actions
73
+
74
+ Use the prebuilt composite action instead — same effect, less typing:
75
+
76
+ ```yaml
77
+ - uses: geos/fixprompt-deploy@v1
78
+ with:
79
+ source: ${{ secrets.FIXPROMPT_SOURCE }}
80
+ key: ${{ secrets.FIXPROMPT_KEY }}
81
+ status: success
82
+ ```
83
+
84
+ ## License
85
+
86
+ UNLICENSED (closed beta).
package/dist/cli.js ADDED
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/detect.ts
4
+ import { execSync } from "child_process";
5
+ function git(args) {
6
+ try {
7
+ return execSync(`git ${args}`, {
8
+ stdio: ["ignore", "pipe", "ignore"]
9
+ }).toString().trim();
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ function gitRemoteToRepoUrl(remoteUrl) {
15
+ if (!remoteUrl) return { repo: null, webBase: null };
16
+ const sshMatch = remoteUrl.match(/git@([^:]+):(.+?)(\.git)?$/);
17
+ if (sshMatch) {
18
+ const [, host, slug] = sshMatch;
19
+ return { repo: slug, webBase: `https://${host}/${slug}` };
20
+ }
21
+ const httpsMatch = remoteUrl.match(/^https?:\/\/([^/]+)\/(.+?)(\.git)?$/);
22
+ if (httpsMatch) {
23
+ const [, host, slug] = httpsMatch;
24
+ return { repo: slug, webBase: `https://${host}/${slug}` };
25
+ }
26
+ return { repo: null, webBase: null };
27
+ }
28
+ function detectFromCi() {
29
+ const env = process.env;
30
+ if (env.GITHUB_ACTIONS === "true") {
31
+ return {
32
+ provider: "github-actions",
33
+ commit_sha: env.GITHUB_SHA ?? "",
34
+ branch: env.GITHUB_REF_NAME ?? null,
35
+ repo: env.GITHUB_REPOSITORY ?? null,
36
+ commit_url: env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_SHA ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/commit/${env.GITHUB_SHA}` : null
37
+ };
38
+ }
39
+ if (env.VERCEL === "1" || env.VERCEL === "true") {
40
+ return {
41
+ provider: "vercel",
42
+ commit_sha: env.VERCEL_GIT_COMMIT_SHA ?? "",
43
+ branch: env.VERCEL_GIT_COMMIT_REF ?? null,
44
+ repo: env.VERCEL_GIT_REPO_OWNER && env.VERCEL_GIT_REPO_SLUG ? `${env.VERCEL_GIT_REPO_OWNER}/${env.VERCEL_GIT_REPO_SLUG}` : null,
45
+ commit_message: env.VERCEL_GIT_COMMIT_MESSAGE ?? null
46
+ };
47
+ }
48
+ if (env.RAILWAY_ENVIRONMENT) {
49
+ return {
50
+ provider: "railway",
51
+ commit_sha: env.RAILWAY_GIT_COMMIT_SHA ?? "",
52
+ branch: env.RAILWAY_GIT_BRANCH ?? null,
53
+ repo: env.RAILWAY_GIT_REPO_OWNER && env.RAILWAY_GIT_REPO_NAME ? `${env.RAILWAY_GIT_REPO_OWNER}/${env.RAILWAY_GIT_REPO_NAME}` : null,
54
+ commit_message: env.RAILWAY_GIT_COMMIT_MESSAGE ?? null
55
+ };
56
+ }
57
+ return {};
58
+ }
59
+ function detectFromGit() {
60
+ const sha = git("rev-parse HEAD");
61
+ const branch = git("rev-parse --abbrev-ref HEAD");
62
+ const remote = git("config --get remote.origin.url");
63
+ const message = git("log -1 --pretty=format:%s");
64
+ const { repo, webBase } = gitRemoteToRepoUrl(remote);
65
+ return {
66
+ provider: "manual",
67
+ commit_sha: sha ?? "",
68
+ branch: branch ?? null,
69
+ repo: repo ?? null,
70
+ commit_message: message ?? null,
71
+ commit_url: webBase && sha ? `${webBase}/commit/${sha}` : null
72
+ };
73
+ }
74
+ function detectContext() {
75
+ const ci = detectFromCi();
76
+ const ciHasSha = typeof ci.commit_sha === "string" && ci.commit_sha.length > 0;
77
+ const git2 = detectFromGit();
78
+ const merged = {
79
+ provider: ci.provider ?? git2.provider ?? "manual",
80
+ commit_sha: (ciHasSha ? ci.commit_sha : git2.commit_sha) ?? "",
81
+ branch: ci.branch ?? git2.branch ?? null,
82
+ repo: ci.repo ?? git2.repo ?? null,
83
+ commit_message: ci.commit_message ?? git2.commit_message ?? null,
84
+ commit_url: ci.commit_url ?? git2.commit_url ?? null
85
+ };
86
+ return merged;
87
+ }
88
+
89
+ // src/version.ts
90
+ var CLI_NAME = "@fixprompt/cli";
91
+ var CLI_VERSION = "0.0.1";
92
+
93
+ // src/cli.ts
94
+ var DEFAULT_ENDPOINT = "https://geosloghub-production.up.railway.app";
95
+ function parseArgs(argv) {
96
+ const flags = {};
97
+ const positional = [];
98
+ let i = 0;
99
+ const command = argv[0] ?? "help";
100
+ for (i = 1; i < argv.length; i++) {
101
+ const tok = argv[i];
102
+ if (tok.startsWith("--")) {
103
+ const eq = tok.indexOf("=");
104
+ if (eq !== -1) {
105
+ flags[tok.slice(2, eq)] = tok.slice(eq + 1);
106
+ } else {
107
+ const next = argv[i + 1];
108
+ if (next && !next.startsWith("--")) {
109
+ flags[tok.slice(2)] = next;
110
+ i++;
111
+ } else {
112
+ flags[tok.slice(2)] = true;
113
+ }
114
+ }
115
+ } else {
116
+ positional.push(tok);
117
+ }
118
+ }
119
+ return { command, flags, positional };
120
+ }
121
+ function helpText() {
122
+ return `${CLI_NAME} v${CLI_VERSION}
123
+
124
+ Usage:
125
+ fixprompt deploy-start [options]
126
+ fixprompt help
127
+
128
+ deploy-start options:
129
+ --source <slug> LOGHUB_SOURCE (default: $LOGHUB_SOURCE or $FIXPROMPT_SOURCE env)
130
+ --key <k_...> LOGHUB_KEY (default: $LOGHUB_KEY or $FIXPROMPT_KEY env)
131
+ --endpoint <url> Broker URL (default: $FIXPROMPT_ENDPOINT or ${DEFAULT_ENDPOINT})
132
+ --status <state> in_progress | success | failed (default: in_progress)
133
+ --commit-sha <sha> Override auto-detected commit
134
+ --branch <name> Override auto-detected branch
135
+ --message <text> Override auto-detected commit subject
136
+
137
+ Auto-detected CI providers: GitHub Actions, Vercel, Railway.
138
+ `;
139
+ }
140
+ function readSource(flags) {
141
+ const v = flags.source ?? process.env.LOGHUB_SOURCE ?? process.env.FIXPROMPT_SOURCE;
142
+ if (!v) {
143
+ console.error("Missing --source (or $LOGHUB_SOURCE / $FIXPROMPT_SOURCE)");
144
+ process.exit(1);
145
+ }
146
+ return v;
147
+ }
148
+ function readKey(flags) {
149
+ const v = flags.key ?? process.env.LOGHUB_KEY ?? process.env.FIXPROMPT_KEY;
150
+ if (!v) {
151
+ console.error("Missing --key (or $LOGHUB_KEY / $FIXPROMPT_KEY)");
152
+ process.exit(1);
153
+ }
154
+ return v;
155
+ }
156
+ function readEndpoint(flags) {
157
+ const v = flags.endpoint ?? process.env.FIXPROMPT_ENDPOINT ?? DEFAULT_ENDPOINT;
158
+ return v.replace(/\/$/, "");
159
+ }
160
+ async function deployStart(args) {
161
+ const source = readSource(args.flags);
162
+ const key = readKey(args.flags);
163
+ const endpoint = readEndpoint(args.flags);
164
+ const status = args.flags.status ?? "in_progress";
165
+ const ctx = detectContext();
166
+ const commit_sha = args.flags["commit-sha"] ?? ctx.commit_sha;
167
+ if (!commit_sha) {
168
+ console.error(
169
+ "Could not detect a commit_sha. Run inside a git working tree or pass --commit-sha."
170
+ );
171
+ process.exit(1);
172
+ }
173
+ const payload = {
174
+ commit_sha,
175
+ branch: args.flags.branch ?? ctx.branch,
176
+ repo: ctx.repo,
177
+ provider: ctx.provider,
178
+ commit_message: args.flags.message ?? ctx.commit_message,
179
+ commit_url: ctx.commit_url,
180
+ fixprompt_version: CLI_VERSION,
181
+ status
182
+ };
183
+ const url = `${endpoint}/deployments`;
184
+ const res = await fetch(url, {
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json",
188
+ "x-loghub-source": source,
189
+ "x-loghub-key": key
190
+ },
191
+ body: JSON.stringify(payload)
192
+ });
193
+ const text = await res.text();
194
+ if (!res.ok) {
195
+ console.error(`\u2717 ${res.status} ${res.statusText}: ${text}`);
196
+ process.exit(1);
197
+ }
198
+ let parsed;
199
+ try {
200
+ parsed = JSON.parse(text);
201
+ } catch {
202
+ parsed = { raw: text };
203
+ }
204
+ console.log(
205
+ `\u2713 Deploy marker sent \u2014 ${commit_sha.slice(0, 7)} on ${payload.branch ?? "?"} via ${payload.provider}`
206
+ );
207
+ if (parsed.id) console.log(` deployment_id: ${parsed.id}`);
208
+ }
209
+ async function main() {
210
+ const args = parseArgs(process.argv.slice(2));
211
+ switch (args.command) {
212
+ case "deploy-start":
213
+ await deployStart(args);
214
+ break;
215
+ case "help":
216
+ case "--help":
217
+ case "-h":
218
+ console.log(helpText());
219
+ break;
220
+ case "version":
221
+ case "--version":
222
+ case "-v":
223
+ console.log(`${CLI_NAME} v${CLI_VERSION}`);
224
+ break;
225
+ default:
226
+ console.error(`Unknown command: ${args.command}
227
+ `);
228
+ console.error(helpText());
229
+ process.exit(1);
230
+ }
231
+ }
232
+ main().catch((err) => {
233
+ console.error(`fatal: ${err.message ?? err}`);
234
+ process.exit(1);
235
+ });
236
+ export {
237
+ deployStart
238
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@fixprompt/cli",
3
+ "version": "0.0.1",
4
+ "description": "FixPrompt CLI — annotate deployments and ship them to the broker so the dashboard knows what changed.",
5
+ "license": "UNLICENSED",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Geos-LLC/FixPrompt.git",
9
+ "directory": "cli"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "fixprompt": "dist/cli.js"
17
+ },
18
+ "files": ["dist", "README.md"],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "vitest run",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepare": "npm run build",
25
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "tsup": "^8.3.5",
30
+ "typescript": "^5.4.0",
31
+ "vitest": "^2.1.0"
32
+ }
33
+ }