@oranix/quiver-cli 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 ADDED
@@ -0,0 +1,62 @@
1
+ # @oranix/quiver-cli
2
+
3
+ Quiver CLI — manage apps, builds, releases from the terminal.
4
+
5
+ Status: **alpha** (P3.4.1 / P3.4.2). See `docs/cli-reference.md` for the
6
+ planned command taxonomy; v1 ships `login`, `logout`, `whoami`, `apps list/get`,
7
+ and `builds list/get`. Other commands listed in the reference land
8
+ incrementally as backend endpoints become available.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ # From the repo root (after `pnpm install` at the top level):
14
+ pnpm --filter @oranix/quiver-cli build
15
+ pnpm --filter @oranix/quiver-cli start -- whoami
16
+
17
+ # Or, once published to npm:
18
+ npm install -g @oranix/quiver-cli
19
+ quiver --help
20
+ ```
21
+
22
+ ## Quickstart
23
+
24
+ ```bash
25
+ # 1. Log in. The CLI prints a URL you must open in a browser.
26
+ quiver login
27
+
28
+ # 2. Verify who you are.
29
+ quiver whoami
30
+
31
+ # 3. List your apps.
32
+ quiver apps list
33
+
34
+ # 4. List builds for an app (by slug or id).
35
+ quiver builds list myapp-android
36
+ ```
37
+
38
+ ## CI mode
39
+
40
+ ```bash
41
+ export QUIVER_API=https://quiver-worker.artin.workers.dev
42
+ export QUIVER_SESSION_COOKIE=... # paste from browser DevTools
43
+ quiver whoami
44
+ quiver builds list myapp-android
45
+ ```
46
+
47
+ ## How auth works (v1)
48
+
49
+ Raft OAuth today only supports the browser-redirect flow with HttpOnly
50
+ cookies. The CLI can't intercept the redirect, so `quiver login` asks
51
+ you to:
52
+
53
+ 1. Open the printed URL in any browser.
54
+ 2. Sign in with Raft.
55
+ 3. Copy the `quiver_session` cookie value from DevTools.
56
+ 4. Paste it back into the CLI.
57
+
58
+ The token is saved to `$XDG_CONFIG_HOME/quiver/auth.json` (mode 0600).
59
+ For CI, pass it via `QUIVER_SESSION_COOKIE` instead.
60
+
61
+ v2 will swap this for a true headless flow (Raft Device Flow or a
62
+ `--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,7 @@
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 registerBuildCommands(program: Command): void;
@@ -0,0 +1,231 @@
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 { existsSync, readFileSync } from "node:fs";
7
+ import { apiRequest, apiUploadFile } from "../lib/api.js";
8
+ export function registerBuildCommands(program) {
9
+ const builds = program
10
+ .command("builds")
11
+ .description("Inspect builds inside an app.");
12
+ builds
13
+ .command("list <appIdOrSlug>")
14
+ .alias("ls")
15
+ .description("List builds for an app.")
16
+ .option("--limit <n>", "Max rows (default 50)", "50")
17
+ .option("--json", "Output JSON.", false)
18
+ .action(async (appIdOrSlug, opts) => {
19
+ const id = await resolveAppId(appIdOrSlug);
20
+ const res = await apiRequest(`/api/apps/${id}/builds`, { query: { limit: opts.limit } });
21
+ if (opts.json) {
22
+ console.log(JSON.stringify(res, null, 2));
23
+ return;
24
+ }
25
+ if (res.builds.length === 0) {
26
+ console.log("No builds yet.");
27
+ return;
28
+ }
29
+ for (const b of res.builds) {
30
+ const flag = b.should_force_update ? " [force]" : "";
31
+ console.log(`${b.version_name} (${b.version_code}) ${b.product_type}/${b.release_type} status=${b.status}${flag} id=${b.id.slice(0, 8)}`);
32
+ }
33
+ });
34
+ builds
35
+ .command("get <appIdOrSlug> <buildId>")
36
+ .description("Show details for a single build.")
37
+ .option("--json", "Output JSON.", false)
38
+ .action(async (appIdOrSlug, buildId, opts) => {
39
+ const id = await resolveAppId(appIdOrSlug);
40
+ const build = await apiRequest(`/api/apps/${id}/builds/${buildId}`);
41
+ if (opts.json) {
42
+ console.log(JSON.stringify(build, null, 2));
43
+ return;
44
+ }
45
+ console.log(`${build.version_name} (${build.version_code})`);
46
+ console.log(` product_type: ${build.product_type}`);
47
+ console.log(` release_type: ${build.release_type}`);
48
+ console.log(` status: ${build.status}`);
49
+ console.log(` should_force_update: ${build.should_force_update ? "yes" : "no"}`);
50
+ console.log(` created_at: ${new Date(build.created_at).toISOString()}`);
51
+ if (build.completed_at) {
52
+ console.log(` completed_at: ${new Date(build.completed_at).toISOString()}`);
53
+ }
54
+ if (build.changelog) {
55
+ console.log(`\n changelog:\n${build.changelog.split("\n").map((l) => " " + l).join("\n")}`);
56
+ }
57
+ });
58
+ builds
59
+ .command("publish-android <appIdOrSlug>")
60
+ .description("Create an Android build/release and upload APK plus support artifacts.")
61
+ .requiredOption("--apk <path>", "Installable APK path.")
62
+ .requiredOption("--version-name <name>", "Android versionName.")
63
+ .requiredOption("--version-code <code>", "Android versionCode.")
64
+ .option("--channel <slug>", "Quiver channel slug.", "main")
65
+ .option("--arch <abi>", "APK ABI/arch metadata.", "arm64-v8a")
66
+ .option("--release-type <type>", "Release type metadata.", "stable")
67
+ .option("--product-type <type>", "Product type metadata.", "android-apk")
68
+ .option("--mapping <path>", "R8/ProGuard mapping.txt support artifact.")
69
+ .option("--symbols <path>", "Native symbols archive support artifact.")
70
+ .option("--metadata <path>", "Build metadata JSON support artifact.")
71
+ .option("--changelog <text>", "Inline changelog.")
72
+ .option("--changelog-file <path>", "Read changelog from file.")
73
+ .option("--source-commit <sha>", "Source commit SHA.")
74
+ .option("--source-branch <branch>", "Source branch.")
75
+ .option("--build-time <iso>", "Build time. Defaults to now.")
76
+ .option("--ci-provider <name>", "CI provider name.")
77
+ .option("--ci-run-id <id>", "CI run id.")
78
+ .option("--ci-url <url>", "CI run URL.")
79
+ .option("--force-update", "Mark release as force update.", false)
80
+ .option("--draft", "Create draft release instead of active.", false)
81
+ .option("--json", "Output JSON.", false)
82
+ .action(async (appIdOrSlug, opts) => {
83
+ const appId = await resolveAppId(appIdOrSlug);
84
+ const channelId = await resolveChannelId(appId, opts.channel);
85
+ const versionCode = Number(opts.versionCode);
86
+ if (!Number.isFinite(versionCode) || versionCode < 0) {
87
+ throw new Error("--version-code must be a non-negative number");
88
+ }
89
+ for (const file of [opts.apk, opts.mapping, opts.symbols, opts.metadata].filter(Boolean)) {
90
+ if (!existsSync(file))
91
+ throw new Error(`missing file: ${file}`);
92
+ }
93
+ const changelog = opts.changelogFile
94
+ ? readFileSync(opts.changelogFile, "utf8")
95
+ : opts.changelog ?? null;
96
+ const metadataJson = opts.metadata
97
+ ? JSON.parse(readFileSync(opts.metadata, "utf8"))
98
+ : {};
99
+ const provenance = {
100
+ source_commit: opts.sourceCommit ?? null,
101
+ source_branch: opts.sourceBranch ?? null,
102
+ build_time: opts.buildTime ?? new Date().toISOString(),
103
+ ci_provider: opts.ciProvider ?? null,
104
+ ci_run_id: opts.ciRunId ?? null,
105
+ ci_url: opts.ciUrl ?? null,
106
+ };
107
+ const build = await apiRequest(`/api/apps/${appId}/builds`, {
108
+ method: "POST",
109
+ body: {
110
+ channel_id: channelId,
111
+ product_type: opts.productType,
112
+ release_type: opts.releaseType,
113
+ version_name: opts.versionName,
114
+ version_code: versionCode,
115
+ changelog,
116
+ source: "cli",
117
+ status: "succeeded",
118
+ build_metadata_json: metadataJson,
119
+ provenance_json: provenance,
120
+ should_force_update: Boolean(opts.forceUpdate),
121
+ },
122
+ });
123
+ const assets = [];
124
+ assets.push(await uploadAndRegisterAsset(appId, build.id, opts.apk, {
125
+ artifact_kind: "installable",
126
+ platform: "android",
127
+ arch: opts.arch,
128
+ filetype: "apk",
129
+ }));
130
+ if (opts.mapping) {
131
+ assets.push(await uploadAndRegisterAsset(appId, build.id, opts.mapping, {
132
+ artifact_kind: "proguard-mapping",
133
+ platform: "android",
134
+ arch: null,
135
+ filetype: "mapping.txt",
136
+ }));
137
+ }
138
+ if (opts.symbols) {
139
+ assets.push(await uploadAndRegisterAsset(appId, build.id, opts.symbols, {
140
+ artifact_kind: "native-symbols",
141
+ platform: "android",
142
+ arch: null,
143
+ filetype: "symbols.zip",
144
+ }));
145
+ }
146
+ if (opts.metadata) {
147
+ assets.push(await uploadAndRegisterAsset(appId, build.id, opts.metadata, {
148
+ artifact_kind: "metadata-file",
149
+ platform: "android",
150
+ arch: null,
151
+ filetype: "metadata.json",
152
+ }));
153
+ }
154
+ const release = await apiRequest(`/api/apps/${appId}/releases`, {
155
+ method: "POST",
156
+ body: {
157
+ build_id: build.id,
158
+ channel_id: channelId,
159
+ product_type: opts.productType,
160
+ release_type: opts.releaseType,
161
+ status: opts.draft ? "draft" : "active",
162
+ changelog,
163
+ should_force_update: Boolean(opts.forceUpdate),
164
+ provenance_json: provenance,
165
+ scopes: [{ scope_type: "full", scope_value: "all" }],
166
+ },
167
+ });
168
+ const result = {
169
+ app_id: appId,
170
+ build_id: build.id,
171
+ release_id: release.id,
172
+ channel: opts.channel,
173
+ version_name: opts.versionName,
174
+ version_code: versionCode,
175
+ assets,
176
+ };
177
+ if (opts.json) {
178
+ console.log(JSON.stringify(result, null, 2));
179
+ return;
180
+ }
181
+ console.log(`Published Android release ${opts.versionName} (${versionCode})`);
182
+ console.log(` build: ${build.id}`);
183
+ console.log(` release: ${release.id}`);
184
+ console.log(` channel: ${opts.channel}`);
185
+ console.log(` assets: ${assets.map((a) => `${a.artifact_kind}:${a.filetype}`).join(", ")}`);
186
+ });
187
+ }
188
+ async function resolveAppId(slugOrId) {
189
+ if (slugOrId.length === 36 && slugOrId.split("-").length === 5) {
190
+ return slugOrId;
191
+ }
192
+ const res = await apiRequest("/api/apps");
193
+ const match = res.apps.find((a) => a.slug === slugOrId);
194
+ if (!match) {
195
+ console.error(`No app with slug '${slugOrId}'.`);
196
+ process.exit(1);
197
+ }
198
+ return match.id;
199
+ }
200
+ async function resolveChannelId(appId, channelSlugOrId) {
201
+ const res = await apiRequest(`/api/apps/${appId}/channels`);
202
+ const match = res.channels.find((channel) => channel.id === channelSlugOrId || channel.slug === channelSlugOrId);
203
+ if (!match) {
204
+ console.error(`No channel '${channelSlugOrId}' for app '${appId}'.`);
205
+ process.exit(1);
206
+ }
207
+ return match.id;
208
+ }
209
+ async function uploadAndRegisterAsset(appId, buildId, filePath, metadata) {
210
+ const uploaded = await apiUploadFile(`/api/apps/${appId}/upload`, filePath);
211
+ const asset = await apiRequest(`/api/apps/${appId}/builds/${buildId}/assets`, {
212
+ method: "POST",
213
+ body: {
214
+ ...metadata,
215
+ r2_key: uploaded.r2_key,
216
+ file_hash: uploaded.file_hash,
217
+ size_bytes: uploaded.size_bytes,
218
+ metadata_json: {
219
+ original_filename: uploaded.original_filename,
220
+ },
221
+ },
222
+ });
223
+ return {
224
+ id: asset.id,
225
+ artifact_kind: metadata.artifact_kind,
226
+ filetype: metadata.filetype,
227
+ file_hash: uploaded.file_hash,
228
+ size_bytes: uploaded.size_bytes,
229
+ };
230
+ }
231
+ //# sourceMappingURL=builds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builds.js","sourceRoot":"","sources":["../../src/commands/builds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `quiver login` — authenticate the CLI.
3
+ *
4
+ * v1 flow (browser-required):
5
+ * 1. CLI prints a URL: https://quiver-worker.../api/auth/login?return_to=...
6
+ * 2. User opens the URL in any browser, signs in with Raft OAuth.
7
+ * 3. After login the Worker redirects to /login/raft/callback which is the
8
+ * admin SPA — at this point the user has a HttpOnly `quiver_session`
9
+ * cookie in their browser for the Worker's origin.
10
+ * 4. User opens DevTools → Application → Cookies → copies the cookie value.
11
+ * 5. User runs `quiver login --token <cookie>` (or pastes when prompted).
12
+ *
13
+ * CI mode: `QUIVER_SESSION_COOKIE=... quiver whoami` — env var is read
14
+ * directly, no file storage.
15
+ *
16
+ * Why not OAuth Device Flow or PKCE? Raft OAuth today only supports the
17
+ * browser redirect flow with HttpOnly cookies; the CLI can't intercept
18
+ * the callback. Headless flow is a v2 (TBD: Raft Device Flow support or
19
+ * dev-token bypass for service users).
20
+ */
21
+ import type { Command } from "commander";
22
+ export declare function registerLoginCommands(program: Command): void;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * `quiver login` — authenticate the CLI.
3
+ *
4
+ * v1 flow (browser-required):
5
+ * 1. CLI prints a URL: https://quiver-worker.../api/auth/login?return_to=...
6
+ * 2. User opens the URL in any browser, signs in with Raft OAuth.
7
+ * 3. After login the Worker redirects to /login/raft/callback which is the
8
+ * admin SPA — at this point the user has a HttpOnly `quiver_session`
9
+ * cookie in their browser for the Worker's origin.
10
+ * 4. User opens DevTools → Application → Cookies → copies the cookie value.
11
+ * 5. User runs `quiver login --token <cookie>` (or pastes when prompted).
12
+ *
13
+ * CI mode: `QUIVER_SESSION_COOKIE=... quiver whoami` — env var is read
14
+ * directly, no file storage.
15
+ *
16
+ * Why not OAuth Device Flow or PKCE? Raft OAuth today only supports the
17
+ * browser redirect flow with HttpOnly cookies; the CLI can't intercept
18
+ * the callback. Headless flow is a v2 (TBD: Raft Device Flow support or
19
+ * dev-token bypass for service users).
20
+ */
21
+ import { createInterface } from "node:readline/promises";
22
+ import { stdin, stdout } from "node:process";
23
+ import { apiRequest, getApiBase, QuiverApiError } from "../lib/api.js";
24
+ import { saveConfig, getConfig } from "../lib/config.js";
25
+ async function promptSecret(message) {
26
+ // Use a raw-mode readline so we can mask input with '*'.
27
+ const rl = createInterface({ input: stdin, output: stdout, terminal: true });
28
+ process.stdout.write(message);
29
+ return new Promise((resolve, reject) => {
30
+ let input = "";
31
+ const onData = (chunk) => {
32
+ const ch = chunk.toString();
33
+ if (ch === "\n" || ch === "\r" || ch === "\u0004") {
34
+ process.stdin.removeListener("data", onData);
35
+ process.stdout.write("\n");
36
+ rl.close();
37
+ resolve(input);
38
+ }
39
+ else if (ch === "\u0003") {
40
+ // Ctrl+C
41
+ process.stdout.write("\n");
42
+ rl.close();
43
+ reject(new Error("cancelled"));
44
+ }
45
+ else if (ch === "\u007f" || ch === "\b") {
46
+ // Backspace
47
+ if (input.length > 0) {
48
+ input = input.slice(0, -1);
49
+ process.stdout.write("\b \b");
50
+ }
51
+ }
52
+ else {
53
+ input += ch;
54
+ process.stdout.write("*");
55
+ }
56
+ };
57
+ process.stdin.on("data", onData);
58
+ });
59
+ }
60
+ export function registerLoginCommands(program) {
61
+ const cmd = program
62
+ .command("login")
63
+ .description("Authenticate the CLI against the Quiver Worker.")
64
+ .option("--token <cookie>", "Paste the quiver_session cookie value (from your browser's DevTools).")
65
+ .option("--api <url>", "Override the Quiver Worker base URL for this login only.")
66
+ .option("--print-url", "Just print the login URL; don't prompt for a token.", false)
67
+ .action(async (opts) => {
68
+ const apiBase = opts.api ?? getApiBase();
69
+ const loginUrl = `${apiBase}/api/auth/login?return_to=${encodeURIComponent("/cli/callback")}`;
70
+ if (opts.printUrl) {
71
+ console.log(loginUrl);
72
+ return;
73
+ }
74
+ console.log("To authenticate the quiver CLI:");
75
+ console.log("");
76
+ console.log(` 1. Open this URL in any browser:`);
77
+ console.log(` ${loginUrl}`);
78
+ console.log("");
79
+ console.log(` 2. Sign in with Raft. You'll land on the admin UI.`);
80
+ console.log(` 3. Open DevTools → Application → Cookies → copy the value of "quiver_session".`);
81
+ console.log(` 4. Paste it below.`);
82
+ console.log("");
83
+ let token = opts.token;
84
+ if (!token) {
85
+ token = await promptSecret("quiver_session cookie value (input is hidden): ");
86
+ if (token.length < 8) {
87
+ console.error("Token looks too short (min 8 chars).");
88
+ process.exit(1);
89
+ }
90
+ }
91
+ // Persist the token + apiBase to config file.
92
+ saveConfig({ apiBase, sessionCookie: token });
93
+ console.log(`✔ Saved to ${configDisplayPath()}`);
94
+ console.log(` API base: ${apiBase}`);
95
+ // Verify the token works by calling /api/auth/me.
96
+ try {
97
+ await apiRequest("/api/auth/me");
98
+ console.log(`✔ Token verified — you're logged in.`);
99
+ }
100
+ catch (e) {
101
+ if (e instanceof QuiverApiError && e.status === 401) {
102
+ console.error(`✘ Token rejected (401). Run \`quiver logout\` and try again.`);
103
+ process.exit(1);
104
+ }
105
+ throw e;
106
+ }
107
+ });
108
+ program
109
+ .command("logout")
110
+ .description("Clear the saved session cookie.")
111
+ .action(() => {
112
+ const cfg = getConfig();
113
+ if (!cfg.sessionCookie) {
114
+ console.log("Not logged in (no saved session cookie).");
115
+ return;
116
+ }
117
+ delete cfg.sessionCookie;
118
+ saveConfig(cfg);
119
+ console.log(`✔ Logged out (token cleared from ${configDisplayPath()}).`);
120
+ });
121
+ }
122
+ function configDisplayPath() {
123
+ // Mirror getConfig's path resolution for display only.
124
+ const xdg = process.env.XDG_CONFIG_HOME;
125
+ const dir = xdg && xdg.length > 0 ? xdg : `${process.env.HOME ?? "~"}/.config`;
126
+ return `${dir}/quiver/auth.json`;
127
+ }
128
+ // silence "unused import" if user has no cookie in env
129
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `quiver whoami` — print the current authenticated account.
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerWhoamiCommand(program: Command): void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `quiver whoami` — print the current authenticated account.
3
+ */
4
+ import { apiRequest, QuiverApiError, getApiBase } from "../lib/api.js";
5
+ import { resolveSessionCookie } from "../lib/config.js";
6
+ export function registerWhoamiCommand(program) {
7
+ program
8
+ .command("whoami")
9
+ .description("Print the currently authenticated account + roles.")
10
+ .option("--json", "Output machine-readable JSON.", false)
11
+ .action(async (opts) => {
12
+ if (!resolveSessionCookie()) {
13
+ console.error("Not logged in. Run `quiver login` first.");
14
+ process.exit(1);
15
+ }
16
+ try {
17
+ const me = await apiRequest("/api/auth/me");
18
+ if (!me.account) {
19
+ console.error("Server returned no account info.");
20
+ process.exit(1);
21
+ }
22
+ if (opts.json) {
23
+ console.log(JSON.stringify(me, null, 2));
24
+ return;
25
+ }
26
+ const a = me.account;
27
+ console.log(`${a.display_name} (${a.principal_type})`);
28
+ console.log(` server: ${a.server_slug ?? a.server_id}`);
29
+ console.log(` username: @${a.username ?? "(none)"}`);
30
+ console.log(` org_id: ${a.org_id ?? "(none)"}`);
31
+ console.log(` org_role: ${a.org_role ?? "(none)"}`);
32
+ console.log(` api: ${getApiBase()}`);
33
+ }
34
+ catch (e) {
35
+ if (e instanceof QuiverApiError && e.status === 401) {
36
+ console.error("Not authenticated. Run `quiver login` to refresh.");
37
+ process.exit(1);
38
+ }
39
+ throw e;
40
+ }
41
+ });
42
+ }
43
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAmBxD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,oDAAoD,CAAC;SACjE,MAAM,CAAC,QAAQ,EAAE,+BAA+B,EAAE,KAAK,CAAC;SACxD,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,EAAE;QACzC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,UAAU,CAAa,cAAc,CAAC,CAAC;YACxD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;gBAChB,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YACD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,YAAY,MAAM,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,cAAc,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACpD,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;gBACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * quiver CLI — entry point.
4
+ *
5
+ * Subcommands are registered via the registry module. This file only
6
+ * handles top-level commander setup + global flags (--api, --json, --verbose).
7
+ *
8
+ * Auth model: the CLI doesn't store long-lived tokens. It uses the same
9
+ * browser-cookie session as the admin SPA. Two ways to "log in":
10
+ * - `quiver login` — prints a URL to visit in any browser; the user
11
+ * completes Raft OAuth there, the Worker sets the quiver_session
12
+ * cookie, the user copies the cookie value back into the CLI.
13
+ * - `quiver login --token <cookie>` — paste an existing session cookie.
14
+ *
15
+ * For CI: `quiver login --token "$QUIVER_SESSION_COOKIE"`.
16
+ *
17
+ * Token storage: ~/.config/quiver/auth.json (or $XDG_CONFIG_HOME/quiver/auth.json).
18
+ */
19
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * quiver CLI — entry point.
4
+ *
5
+ * Subcommands are registered via the registry module. This file only
6
+ * handles top-level commander setup + global flags (--api, --json, --verbose).
7
+ *
8
+ * Auth model: the CLI doesn't store long-lived tokens. It uses the same
9
+ * browser-cookie session as the admin SPA. Two ways to "log in":
10
+ * - `quiver login` — prints a URL to visit in any browser; the user
11
+ * completes Raft OAuth there, the Worker sets the quiver_session
12
+ * cookie, the user copies the cookie value back into the CLI.
13
+ * - `quiver login --token <cookie>` — paste an existing session cookie.
14
+ *
15
+ * For CI: `quiver login --token "$QUIVER_SESSION_COOKIE"`.
16
+ *
17
+ * Token storage: ~/.config/quiver/auth.json (or $XDG_CONFIG_HOME/quiver/auth.json).
18
+ */
19
+ import { Command } from "commander";
20
+ import { registerAppCommands } from "./commands/apps.js";
21
+ import { registerBuildCommands } from "./commands/builds.js";
22
+ import { registerLoginCommands } from "./commands/login.js";
23
+ import { registerWhoamiCommand } from "./commands/whoami.js";
24
+ import { getConfig } from "./lib/config.js";
25
+ import { setApiBase } from "./lib/api.js";
26
+ const program = new Command();
27
+ program
28
+ .name("quiver")
29
+ .description("Quiver CLI — manage apps, builds, releases from the terminal.")
30
+ .version("0.1.0")
31
+ .option("--api <url>", "Quiver Worker base URL (default: https://quiver-worker.artin.workers.dev or $QUIVER_API)")
32
+ .option("--json", "Output machine-readable JSON (suppresses human output)", false)
33
+ .option("--verbose", "Print HTTP request details for debugging", false);
34
+ // Re-read global options after parse to wire into the API client.
35
+ program.hook("preAction", (thisCommand) => {
36
+ const opts = thisCommand.opts();
37
+ const cfg = getConfig();
38
+ const apiBase = opts.api ?? process.env.QUIVER_API ?? cfg.apiBase;
39
+ if (apiBase)
40
+ setApiBase(apiBase);
41
+ if (opts.verbose)
42
+ process.env.QUIVER_VERBOSE = "1";
43
+ });
44
+ // --- Subcommand groups ---
45
+ registerLoginCommands(program);
46
+ registerWhoamiCommand(program);
47
+ registerAppCommands(program);
48
+ registerBuildCommands(program);
49
+ program
50
+ .command("version")
51
+ .description("Print quiver CLI version")
52
+ .action(() => {
53
+ console.log(program.version());
54
+ });
55
+ program.parseAsync(process.argv).catch((err) => {
56
+ if (err instanceof Error && err.name === "QuiverApiError") {
57
+ console.error(`error: ${err.message}`);
58
+ if (process.env.QUIVER_VERBOSE === "1" && err.stack) {
59
+ console.error(err.stack);
60
+ }
61
+ process.exit(1);
62
+ }
63
+ console.error(err instanceof Error ? err.message : String(err));
64
+ process.exit(1);
65
+ });
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CACL,aAAa,EACb,0FAA0F,CAC3F;KACA,MAAM,CAAC,QAAQ,EAAE,wDAAwD,EAAE,KAAK,CAAC;KACjF,MAAM,CAAC,WAAW,EAAE,0CAA0C,EAAE,KAAK,CAAC,CAAC;AAE1E,kEAAkE;AAClE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAuD,CAAC;IACrF,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,OAAO,CAAC;IAClE,IAAI,OAAO;QAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,4BAA4B;AAC5B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAC1D,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACpD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * HTTP client for the Quiver Worker API.
3
+ *
4
+ * Mirrors the shape of admin/src/lib/api.ts but uses a CLI cookie instead
5
+ * of the browser's `credentials: "include"` mechanism.
6
+ *
7
+ * Endpoints called by the CLI use `requireAppRole("viewer")` or
8
+ * `requireOrgRole("member")` — they accept the HttpOnly session cookie
9
+ * as long as the user has been logged in via `quiver login`.
10
+ */
11
+ export declare class QuiverApiError extends Error {
12
+ readonly status: number;
13
+ readonly body: unknown;
14
+ constructor(status: number, body: unknown, message: string);
15
+ }
16
+ export declare function setApiBase(url: string): void;
17
+ export declare function getApiBase(): string;
18
+ export interface ApiRequestOptions {
19
+ method?: "GET" | "POST" | "PATCH" | "DELETE";
20
+ body?: unknown;
21
+ query?: Record<string, string | number | boolean | null | undefined>;
22
+ raw?: boolean;
23
+ }
24
+ export declare function apiRequest<T = unknown>(path: string, opts?: ApiRequestOptions): Promise<T>;
25
+ export declare function apiUploadFile<T = unknown>(path: string, filePath: string, fieldName?: string): Promise<T>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * HTTP client for the Quiver Worker API.
3
+ *
4
+ * Mirrors the shape of admin/src/lib/api.ts but uses a CLI cookie instead
5
+ * of the browser's `credentials: "include"` mechanism.
6
+ *
7
+ * Endpoints called by the CLI use `requireAppRole("viewer")` or
8
+ * `requireOrgRole("member")` — they accept the HttpOnly session cookie
9
+ * as long as the user has been logged in via `quiver login`.
10
+ */
11
+ import { resolveApiBase, resolveSessionCookie } from "./config.js";
12
+ import { Blob } from "node:buffer";
13
+ import { readFile } from "node:fs/promises";
14
+ import { basename } from "node:path";
15
+ export class QuiverApiError extends Error {
16
+ status;
17
+ body;
18
+ constructor(status, body, message) {
19
+ super(message);
20
+ this.name = "QuiverApiError";
21
+ this.status = status;
22
+ this.body = body;
23
+ }
24
+ }
25
+ let currentApiBase = null;
26
+ export function setApiBase(url) {
27
+ currentApiBase = url;
28
+ }
29
+ export function getApiBase() {
30
+ return currentApiBase ?? resolveApiBase();
31
+ }
32
+ export async function apiRequest(path, opts = {}) {
33
+ const url = new URL(path.startsWith("/") ? path : `/${path}`, getApiBase());
34
+ if (opts.query) {
35
+ for (const [k, v] of Object.entries(opts.query)) {
36
+ if (v === null || v === undefined)
37
+ continue;
38
+ url.searchParams.set(k, String(v));
39
+ }
40
+ }
41
+ const headers = {
42
+ accept: "application/json",
43
+ };
44
+ const bearer = process.env.QUIVER_AUTH_TOKEN ?? process.env.QUIVER_BEARER_TOKEN;
45
+ if (bearer)
46
+ headers.authorization = `Bearer ${bearer}`;
47
+ const cookie = resolveSessionCookie();
48
+ if (!bearer && cookie)
49
+ headers["cookie"] = cookie;
50
+ let body;
51
+ if (opts.body !== undefined) {
52
+ headers["content-type"] = "application/json";
53
+ body = JSON.stringify(opts.body);
54
+ }
55
+ const res = await fetch(url.toString(), {
56
+ method: opts.method ?? "GET",
57
+ headers,
58
+ ...(body !== undefined ? { body } : {}),
59
+ });
60
+ if (process.env.QUIVER_VERBOSE === "1") {
61
+ console.error(`> ${opts.method ?? "GET"} ${url}`);
62
+ console.error(`< ${res.status}`);
63
+ }
64
+ if (opts.raw)
65
+ return res;
66
+ const text = await res.text();
67
+ let data = text;
68
+ if (text.length > 0) {
69
+ try {
70
+ data = JSON.parse(text);
71
+ }
72
+ catch {
73
+ // keep as text
74
+ }
75
+ }
76
+ if (!res.ok) {
77
+ const msg = typeof data === "object" && data && "error" in data
78
+ ? String(data.error)
79
+ : `${res.status} ${res.statusText}`;
80
+ throw new QuiverApiError(res.status, data, msg);
81
+ }
82
+ return data;
83
+ }
84
+ export async function apiUploadFile(path, filePath, fieldName = "apk") {
85
+ const url = new URL(path.startsWith("/") ? path : `/${path}`, getApiBase());
86
+ const cookie = resolveSessionCookie();
87
+ const form = new FormData();
88
+ const bytes = await readFile(filePath);
89
+ form.append(fieldName, new Blob([bytes]), basename(filePath));
90
+ const headers = {
91
+ accept: "application/json",
92
+ };
93
+ const bearer = process.env.QUIVER_AUTH_TOKEN ?? process.env.QUIVER_BEARER_TOKEN;
94
+ if (bearer)
95
+ headers.authorization = `Bearer ${bearer}`;
96
+ if (!bearer && cookie)
97
+ headers.cookie = cookie;
98
+ const res = await fetch(url.toString(), {
99
+ method: "POST",
100
+ headers,
101
+ body: form,
102
+ });
103
+ if (process.env.QUIVER_VERBOSE === "1") {
104
+ console.error(`> POST ${url}`);
105
+ console.error(`< ${res.status}`);
106
+ }
107
+ const text = await res.text();
108
+ let data = text;
109
+ if (text.length > 0) {
110
+ try {
111
+ data = JSON.parse(text);
112
+ }
113
+ catch {
114
+ // keep as text
115
+ }
116
+ }
117
+ if (!res.ok) {
118
+ const msg = typeof data === "object" && data && "error" in data
119
+ ? String(data.error)
120
+ : `${res.status} ${res.statusText}`;
121
+ throw new QuiverApiError(res.status, data, msg);
122
+ }
123
+ return data;
124
+ }
125
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Config + auth storage for the quiver CLI.
3
+ *
4
+ * Resolution order for any setting (first wins):
5
+ * 1. CLI flag (--api, --token, ...)
6
+ * 2. Environment variable (QUIVER_API, QUIVER_SESSION_COOKIE, ...)
7
+ * 3. File at $XDG_CONFIG_HOME/quiver/auth.json (default ~/.config/quiver/auth.json)
8
+ *
9
+ * The config file holds:
10
+ * - apiBase: the Quiver Worker URL the CLI talks to
11
+ * - sessionCookie: the HttpOnly `quiver_session` cookie value the
12
+ * Worker set after `quiver login` (or copied from the browser DevTools)
13
+ */
14
+ export interface CliConfig {
15
+ apiBase?: string;
16
+ sessionCookie?: string;
17
+ }
18
+ export declare function getConfig(): CliConfig;
19
+ export declare function saveConfig(patch: Partial<CliConfig>): CliConfig;
20
+ export declare function clearConfig(): void;
21
+ export declare function resolveApiBase(): string;
22
+ export declare function resolveSessionCookie(): string | undefined;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Config + auth storage for the quiver CLI.
3
+ *
4
+ * Resolution order for any setting (first wins):
5
+ * 1. CLI flag (--api, --token, ...)
6
+ * 2. Environment variable (QUIVER_API, QUIVER_SESSION_COOKIE, ...)
7
+ * 3. File at $XDG_CONFIG_HOME/quiver/auth.json (default ~/.config/quiver/auth.json)
8
+ *
9
+ * The config file holds:
10
+ * - apiBase: the Quiver Worker URL the CLI talks to
11
+ * - sessionCookie: the HttpOnly `quiver_session` cookie value the
12
+ * Worker set after `quiver login` (or copied from the browser DevTools)
13
+ */
14
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { homedir } from "node:os";
17
+ const DEFAULT_API_BASE = "https://quiver-worker.artin.workers.dev";
18
+ function configPath() {
19
+ const xdg = process.env.XDG_CONFIG_HOME;
20
+ const dir = xdg && xdg.length > 0 ? xdg : join(homedir(), ".config");
21
+ return join(dir, "quiver", "auth.json");
22
+ }
23
+ export function getConfig() {
24
+ const path = configPath();
25
+ if (!existsSync(path))
26
+ return {};
27
+ try {
28
+ const raw = readFileSync(path, "utf8");
29
+ const parsed = JSON.parse(raw);
30
+ return parsed ?? {};
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ }
36
+ export function saveConfig(patch) {
37
+ const current = getConfig();
38
+ const next = { ...current, ...patch };
39
+ const path = configPath();
40
+ mkdirSync(dirname(path), { recursive: true });
41
+ // 0600 — session cookie is sensitive.
42
+ writeFileSync(path, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
43
+ return next;
44
+ }
45
+ export function clearConfig() {
46
+ const current = getConfig();
47
+ const next = { ...current };
48
+ delete next.sessionCookie;
49
+ saveConfig(next);
50
+ }
51
+ export function resolveApiBase() {
52
+ const cliFlag = process.env.QUIVER_CLI_API;
53
+ if (cliFlag)
54
+ return cliFlag;
55
+ const env = process.env.QUIVER_API;
56
+ if (env)
57
+ return env;
58
+ const cfg = getConfig();
59
+ if (cfg.apiBase)
60
+ return cfg.apiBase;
61
+ return DEFAULT_API_BASE;
62
+ }
63
+ export function resolveSessionCookie() {
64
+ // CI mode wins over file config (env vars are explicit).
65
+ const env = process.env.QUIVER_SESSION_COOKIE;
66
+ if (env)
67
+ return env;
68
+ return getConfig().sessionCookie;
69
+ }
70
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOlC,MAAM,gBAAgB,GAAG,yCAAyC,CAAC;AAEnE,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACxC,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACrE,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QAC5C,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAc,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,sCAAsC;IACtC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IACvC,OAAO,IAAI,CAAC,aAAa,CAAC;IAC1B,UAAU,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC3C,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACnC,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,IAAI,GAAG,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IACpC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,yDAAyD;IACzD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC9C,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,OAAO,SAAS,EAAE,CAAC,aAAa,CAAC;AACnC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@oranix/quiver-cli",
3
+ "version": "0.1.0",
4
+ "description": "Quiver CLI — manage apps, builds, releases from the terminal.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "bin": {
9
+ "quiver": "./dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsx watch src/index.ts",
18
+ "start": "node dist/index.js",
19
+ "test": "vitest run",
20
+ "lint": "eslint src/",
21
+ "lint:fix": "eslint --fix src/"
22
+ },
23
+ "dependencies": {
24
+ "commander": "^12.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.10.0",
28
+ "eslint": "^9.17.0",
29
+ "tsx": "^4.19.2",
30
+ "typescript": "^5.9.3",
31
+ "vitest": "^2.1.9"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ }
36
+ }