@botiverse/hands-cli 0.4.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 ADDED
@@ -0,0 +1,71 @@
1
+ # @botiverse/hands-cli
2
+
3
+ Hands CLI — manage apps, builds, releases from the terminal.
4
+
5
+ Status: **alpha**. The npm package is public as `@botiverse/hands-cli`; v1 ships
6
+ `login`, `logout`, `whoami`, `apps list/get`, `builds list/get`, and
7
+ `builds publish-android`. Other commands listed in `docs/cli-reference.md` land
8
+ incrementally as backend endpoints become available.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g @botiverse/hands-cli
14
+ hands --help
15
+
16
+ # Or run without installing globally:
17
+ npm exec --package @botiverse/hands-cli@0.3.2 -- hands --help
18
+
19
+ # Local repo development:
20
+ pnpm --filter @botiverse/hands-cli build
21
+ pnpm --filter @botiverse/hands-cli start -- whoami
22
+ ```
23
+
24
+ ## Quickstart
25
+
26
+ ```bash
27
+ # 1. Log in. The CLI prints a URL you must open in a browser.
28
+ hands login
29
+
30
+ # 2. Verify who you are.
31
+ hands whoami
32
+
33
+ # 3. List your apps.
34
+ hands apps list
35
+
36
+ # 4. List builds for an app (by slug or id).
37
+ hands builds list myapp-android
38
+
39
+ # 5. Publish an Android APK release.
40
+ hands builds publish-android raft-android \
41
+ --channel main \
42
+ --apk ./app-release.apk \
43
+ --version-name 1.0.3 \
44
+ --version-code 1000300
45
+ ```
46
+
47
+ ## CI mode
48
+
49
+ ```bash
50
+ export QUIVER_API=https://quiver.oranix.io
51
+ export QUIVER_SESSION_COOKIE=... # paste from browser DevTools
52
+ hands whoami
53
+ hands builds list myapp-android
54
+ ```
55
+
56
+ ## How auth works (v1)
57
+
58
+ Raft OAuth today only supports the browser-redirect flow with HttpOnly
59
+ cookies. The CLI can't intercept the redirect, so `hands login` asks
60
+ you to:
61
+
62
+ 1. Open the printed URL in any browser.
63
+ 2. Sign in with Raft.
64
+ 3. Copy the `quiver_session` cookie value from DevTools.
65
+ 4. Paste it back into the CLI.
66
+
67
+ The token is saved to `$XDG_CONFIG_HOME/quiver/auth.json` (mode 0600).
68
+ For CI, pass it via `QUIVER_SESSION_COOKIE` instead.
69
+
70
+ v2 will swap this for a true headless flow (Raft Device Flow or a
71
+ `--token-stdin` service-user mode). See `publish-tasks.md` P3.4.x.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `quiver apps` — list / inspect apps in the caller's org.
3
+ *
4
+ * Wires GET /api/apps + GET /api/apps/:appId.
5
+ */
6
+ import type { Command } from "commander";
7
+ export declare function registerAppCommands(program: Command): void;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `quiver apps` — list / inspect apps in the caller's org.
3
+ *
4
+ * Wires GET /api/apps + GET /api/apps/:appId.
5
+ */
6
+ import { apiRequest } from "../lib/api.js";
7
+ export function registerAppCommands(program) {
8
+ const apps = program.command("apps").description("Manage apps in your org.");
9
+ apps
10
+ .command("list")
11
+ .alias("ls")
12
+ .description("List apps in the current org.")
13
+ .option("--include-archived", "Include archived (soft-deleted) apps.", false)
14
+ .option("--json", "Output JSON.", false)
15
+ .action(async (opts) => {
16
+ const res = await apiRequest("/api/apps", {
17
+ query: { include_archived: opts.includeArchived ? "1" : "0" },
18
+ });
19
+ if (opts.json) {
20
+ console.log(JSON.stringify(res, null, 2));
21
+ return;
22
+ }
23
+ if (res.apps.length === 0) {
24
+ console.log("No apps. Create one in the admin UI first.");
25
+ return;
26
+ }
27
+ console.log(["SLUG", "PLATFORM", "ARCHIVED", "DEFAULT_CHANNEL", "ID"].join("\t"));
28
+ for (const a of res.apps) {
29
+ console.log([
30
+ a.slug,
31
+ a.platform,
32
+ a.archived ? "yes" : "no",
33
+ a.default_channel_slug ?? "—",
34
+ a.id.slice(0, 8),
35
+ ].join("\t"));
36
+ }
37
+ });
38
+ apps
39
+ .command("get <appIdOrSlug>")
40
+ .description("Show details for a single app.")
41
+ .option("--json", "Output JSON.", false)
42
+ .action(async (appIdOrSlug, opts) => {
43
+ // The Worker endpoint accepts UUID; for slug we list + filter.
44
+ // Cheap heuristic: 36-char with dashes = UUID.
45
+ const isUuid = appIdOrSlug.length === 36 && appIdOrUuidDash(appIdOrSlug);
46
+ let id = appIdOrSlug;
47
+ if (!isUuid) {
48
+ const res = await apiRequest("/api/apps");
49
+ const match = res.apps.find((a) => a.slug === appIdOrSlug);
50
+ if (!match) {
51
+ console.error(`No app with slug '${appIdOrSlug}'.`);
52
+ process.exit(1);
53
+ }
54
+ id = match.id;
55
+ }
56
+ const app = await apiRequest(`/api/apps/${id}`);
57
+ if (opts.json) {
58
+ console.log(JSON.stringify(app, null, 2));
59
+ return;
60
+ }
61
+ console.log(`${app.name} (${app.platform})`);
62
+ console.log(` id: ${app.id}`);
63
+ console.log(` slug: ${app.slug}`);
64
+ console.log(` archived: ${app.archived ? "yes" : "no"}`);
65
+ console.log(` default_channel: ${app.default_channel_slug ?? "(none)"}`);
66
+ });
67
+ }
68
+ function appIdOrUuidDash(s) {
69
+ return s.split("-").length === 5;
70
+ }
71
+ //# sourceMappingURL=apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `quiver builds` — list / inspect builds inside an app.
3
+ *
4
+ * Wires GET /api/apps/:appId/builds + GET /api/apps/:appId/builds/:buildId.
5
+ */
6
+ import type { Command } from "commander";
7
+ export declare function parseChangelogOptions(opts: {
8
+ changelog?: string | string[];
9
+ changelogFile?: string | string[];
10
+ }): string | null;
11
+ export declare function registerBuildCommands(program: Command): void;
12
+ export declare function inferElectronPlatform(filePath: string | undefined): string;
13
+ export declare function inferElectronFiletype(filePath: string): string;
14
+ export declare function inferIosFiletype(filePath: string): string;