@farthershore/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.
Files changed (44) hide show
  1. package/README.md +83 -0
  2. package/dist/auth.d.ts +1 -0
  3. package/dist/auth.js +18 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/client.d.ts +74 -0
  6. package/dist/client.js +71 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/commands/billing.d.ts +3 -0
  9. package/dist/commands/billing.js +32 -0
  10. package/dist/commands/billing.js.map +1 -0
  11. package/dist/commands/init.d.ts +3 -0
  12. package/dist/commands/init.js +42 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/login.d.ts +2 -0
  15. package/dist/commands/login.js +82 -0
  16. package/dist/commands/login.js.map +1 -0
  17. package/dist/commands/plans.d.ts +3 -0
  18. package/dist/commands/plans.js +205 -0
  19. package/dist/commands/plans.js.map +1 -0
  20. package/dist/commands/products.d.ts +3 -0
  21. package/dist/commands/products.js +77 -0
  22. package/dist/commands/products.js.map +1 -0
  23. package/dist/commands/team.d.ts +3 -0
  24. package/dist/commands/team.js +47 -0
  25. package/dist/commands/team.js.map +1 -0
  26. package/dist/commands/tokens.d.ts +3 -0
  27. package/dist/commands/tokens.js +31 -0
  28. package/dist/commands/tokens.js.map +1 -0
  29. package/dist/commands/usage.d.ts +3 -0
  30. package/dist/commands/usage.js +24 -0
  31. package/dist/commands/usage.js.map +1 -0
  32. package/dist/config.d.ts +9 -0
  33. package/dist/config.js +74 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +58 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/output.d.ts +11 -0
  39. package/dist/output.js +48 -0
  40. package/dist/output.js.map +1 -0
  41. package/dist/types.d.ts +86 -0
  42. package/dist/types.js +10 -0
  43. package/dist/types.js.map +1 -0
  44. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # @farthershore/cli
2
+
3
+ FartherShore CLI — manage API products, plans, and billing from the terminal.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @farthershore/cli
9
+ ```
10
+
11
+ ## Authentication
12
+
13
+ ```bash
14
+ # Option 1: Interactive login
15
+ farthershore login
16
+
17
+ # Option 2: Environment variable (CI/CD)
18
+ export FARTHERSHORE_TOKEN=mk_xxx.yyy
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # List your products
25
+ farthershore products list
26
+
27
+ # Create a product
28
+ farthershore products create "Weather API" --base-url https://api.weather.com
29
+
30
+ # Pull plan config
31
+ farthershore plans pull --product <product-id>
32
+
33
+ # Edit plans.yaml, then push
34
+ farthershore plans push --product <product-id>
35
+
36
+ # Check Stripe status
37
+ farthershore billing status
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ | Command | Description |
43
+ |---------|-------------|
44
+ | `farthershore login` | Authenticate with FartherShore |
45
+ | `farthershore whoami` | Show current auth context |
46
+ | `farthershore products list` | List products |
47
+ | `farthershore products create` | Create a draft product |
48
+ | `farthershore products publish` | Publish a product |
49
+ | `farthershore plans pull` | Export plan config to YAML |
50
+ | `farthershore plans push` | Validate, diff, and apply config |
51
+ | `farthershore plans diff` | Preview changes without applying |
52
+ | `farthershore tokens list` | List API tokens |
53
+ | `farthershore team invite` | Invite a team member |
54
+ | `farthershore billing connect` | Connect Stripe |
55
+ | `farthershore usage` | View usage analytics |
56
+
57
+ ## Plan Config as Code
58
+
59
+ ```bash
60
+ # Pull current config
61
+ farthershore plans pull --product weather-api
62
+
63
+ # Edit the YAML
64
+ vim ~/.farthershore/configs/weather-api/plans.yaml
65
+
66
+ # See what would change
67
+ farthershore plans diff --product weather-api
68
+
69
+ # Apply changes
70
+ farthershore plans push --product weather-api
71
+
72
+ # CI mode (no confirmation)
73
+ farthershore plans push --product weather-api --yes
74
+ ```
75
+
76
+ ## Global Flags
77
+
78
+ | Flag | Description |
79
+ |------|-------------|
80
+ | `--token <token>` | Override auth token |
81
+ | `--api-url <url>` | Override API base URL |
82
+ | `--format json\|table` | Output format |
83
+ | `--yes` | Skip confirmation prompts |
package/dist/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function resolveToken(overrideToken?: string): string;
package/dist/auth.js ADDED
@@ -0,0 +1,18 @@
1
+ // Token resolution: env var → credentials file → error
2
+ import { loadCredentials } from "./config.js";
3
+ import { CliError } from "./types.js";
4
+ export function resolveToken(overrideToken) {
5
+ // 1. Explicit override (--token flag)
6
+ if (overrideToken)
7
+ return overrideToken;
8
+ // 2. Environment variable
9
+ const envToken = process.env.FARTHERSHORE_TOKEN;
10
+ if (envToken)
11
+ return envToken;
12
+ // 3. Stored credentials (from `farthershore login`)
13
+ const creds = loadCredentials();
14
+ if (creds?.token)
15
+ return creds.token;
16
+ throw new CliError("Not authenticated. Run `farthershore login` or set FARTHERSHORE_TOKEN environment variable.");
17
+ }
18
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,UAAU,YAAY,CAAC,aAAsB;IACjD,sCAAsC;IACtC,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAChD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,oDAAoD;IACpD,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,KAAK,EAAE,KAAK;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IAErC,MAAM,IAAI,QAAQ,CAChB,6FAA6F,CAC9F,CAAC;AACJ,CAAC"}
@@ -0,0 +1,74 @@
1
+ import type { Product, Plan, ApplyResponse, MakerToken, OrgMember, InitProductResponse } from "./types.js";
2
+ export declare function createClient(opts: {
3
+ apiUrl: string;
4
+ token: string;
5
+ }): {
6
+ bootstrap: () => Promise<{
7
+ user: {
8
+ id: string;
9
+ clerkUserId: string;
10
+ };
11
+ activeOrganization: {
12
+ id: string;
13
+ name?: string;
14
+ slug?: string;
15
+ };
16
+ memberships: Array<{
17
+ organization: {
18
+ id: string;
19
+ name?: string;
20
+ slug?: string;
21
+ };
22
+ role: string;
23
+ }>;
24
+ }>;
25
+ validateToken: () => Promise<{
26
+ valid: boolean;
27
+ type: string;
28
+ orgId: string;
29
+ productId: string | null;
30
+ permissions: string[];
31
+ }>;
32
+ listProducts: () => Promise<Product[]>;
33
+ getProduct: (id: string) => Promise<Product>;
34
+ createProduct: (data: {
35
+ name: string;
36
+ baseUrl: string;
37
+ description?: string;
38
+ }) => Promise<Product>;
39
+ updateProduct: (id: string, data: Record<string, unknown>) => Promise<Product>;
40
+ deleteProduct: (id: string) => Promise<void>;
41
+ publishProduct: (id: string) => Promise<Product>;
42
+ initProduct: (data: {
43
+ name: string;
44
+ baseUrl?: string;
45
+ description?: string;
46
+ displayName?: string;
47
+ billingStrategy?: string;
48
+ }) => Promise<InitProductResponse>;
49
+ listPlans: (productId: string) => Promise<Plan[]>;
50
+ createPlan: (productId: string, data: Record<string, unknown>) => Promise<Plan>;
51
+ updatePlan: (productId: string, planId: string, data: Record<string, unknown>) => Promise<Plan>;
52
+ deletePlan: (productId: string, planId: string) => Promise<void>;
53
+ exportPlanConfig: (productId: string) => Promise<string>;
54
+ applyPlanConfig: (productId: string, config: string, opts?: {
55
+ configHash?: string;
56
+ force?: boolean;
57
+ validateOnly?: boolean;
58
+ }) => Promise<ApplyResponse>;
59
+ listTokens: () => Promise<MakerToken[]>;
60
+ revokeToken: (id: string) => Promise<void>;
61
+ listMembers: (orgId: string) => Promise<OrgMember[]>;
62
+ inviteMember: (orgId: string, email: string, role: string) => Promise<unknown>;
63
+ removeMember: (orgId: string, userId: string) => Promise<void>;
64
+ getStripeStatus: () => Promise<{
65
+ connected: boolean;
66
+ chargesEnabled?: boolean;
67
+ accountId?: string;
68
+ }>;
69
+ startStripeOnboard: () => Promise<{
70
+ url: string;
71
+ }>;
72
+ getUsage: (productId: string) => Promise<Record<string, unknown>>;
73
+ };
74
+ export type ApiClient = ReturnType<typeof createClient>;
package/dist/client.js ADDED
@@ -0,0 +1,71 @@
1
+ // Typed API client for the FartherShore platform
2
+ import { CliError } from "./types.js";
3
+ export function createClient(opts) {
4
+ async function request(method, path, body) {
5
+ const res = await fetch(`${opts.apiUrl}${path}`, {
6
+ method,
7
+ headers: {
8
+ Authorization: `Bearer ${opts.token}`,
9
+ "Content-Type": "application/json",
10
+ },
11
+ body: body ? JSON.stringify(body) : undefined,
12
+ });
13
+ if (!res.ok) {
14
+ const err = await res.json().catch(() => ({ error: res.statusText }));
15
+ throw new CliError(err.error ?? `API error: ${res.status}`, res.status);
16
+ }
17
+ if (res.status === 204)
18
+ return undefined;
19
+ return res.json();
20
+ }
21
+ async function rawText(method, path) {
22
+ const res = await fetch(`${opts.apiUrl}${path}`, {
23
+ method,
24
+ headers: { Authorization: `Bearer ${opts.token}` },
25
+ });
26
+ if (!res.ok) {
27
+ throw new CliError(`API error: ${res.status}`, res.status);
28
+ }
29
+ return res.text();
30
+ }
31
+ return {
32
+ // --- Auth ---
33
+ bootstrap: () => request("POST", "/builder/context/bootstrap"),
34
+ validateToken: () => request("POST", "/auth/validate-token"),
35
+ // --- Products ---
36
+ listProducts: () => request("GET", "/products"),
37
+ getProduct: (id) => request("GET", `/products/${id}`),
38
+ createProduct: (data) => request("POST", "/products", data),
39
+ updateProduct: (id, data) => request("PATCH", `/products/${id}`, data),
40
+ deleteProduct: (id) => request("DELETE", `/products/${id}`),
41
+ publishProduct: (id) => request("POST", `/products/${id}/publish`),
42
+ initProduct: (data) => request("POST", "/products/init", data),
43
+ // --- Plans ---
44
+ listPlans: (productId) => request("GET", `/products/${productId}/plans`),
45
+ createPlan: (productId, data) => request("POST", `/products/${productId}/plans`, data),
46
+ updatePlan: (productId, planId, data) => request("PATCH", `/products/${productId}/plans/${planId}`, data),
47
+ deletePlan: (productId, planId) => request("DELETE", `/products/${productId}/plans/${planId}`),
48
+ // --- Plan config (declarative) ---
49
+ exportPlanConfig: (productId) => rawText("GET", `/products/${productId}/plans/export`),
50
+ applyPlanConfig: (productId, config, opts) => {
51
+ const params = opts?.validateOnly ? "?validate_only=true" : "";
52
+ return request("POST", `/products/${productId}/plans/apply${params}`, { config, configHash: opts?.configHash, force: opts?.force });
53
+ },
54
+ // --- Tokens ---
55
+ listTokens: () => request("GET", "/maker-tokens"),
56
+ revokeToken: (id) => request("DELETE", `/maker-tokens/${id}`),
57
+ // --- Team ---
58
+ listMembers: (orgId) => request("GET", `/builder/organizations/${orgId}/members`),
59
+ inviteMember: (orgId, email, role) => request("POST", `/builder/organizations/${orgId}/invitations`, {
60
+ email,
61
+ role,
62
+ }),
63
+ removeMember: (orgId, userId) => request("DELETE", `/builder/organizations/${orgId}/members/${userId}`),
64
+ // --- Billing ---
65
+ getStripeStatus: () => request("GET", "/builder/stripe/status"),
66
+ startStripeOnboard: () => request("POST", "/builder/stripe/onboard"),
67
+ // --- Usage ---
68
+ getUsage: (productId) => request("GET", `/products/${productId}/usage`),
69
+ };
70
+ }
71
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { ApiClient } from "../client.js";
3
+ export declare function registerBillingCommands(program: Command, getClient: () => ApiClient): void;
@@ -0,0 +1,32 @@
1
+ import * as output from "../output.js";
2
+ export function registerBillingCommands(program, getClient) {
3
+ const billing = program
4
+ .command("billing")
5
+ .description("Stripe billing integration");
6
+ billing
7
+ .command("status")
8
+ .description("Show Stripe connection status")
9
+ .action(async () => {
10
+ const client = getClient();
11
+ const status = await client.getStripeStatus();
12
+ if (status.connected) {
13
+ output.success("Stripe connected");
14
+ console.log(` Account: ${status.accountId}`);
15
+ console.log(` Charges enabled: ${status.chargesEnabled ? "✓" : "✗"}`);
16
+ }
17
+ else {
18
+ output.warn("Stripe not connected.");
19
+ output.info("Run `farthershore billing connect` to start onboarding.");
20
+ }
21
+ });
22
+ billing
23
+ .command("connect")
24
+ .description("Start Stripe Connect onboarding (opens browser)")
25
+ .action(async () => {
26
+ const client = getClient();
27
+ const result = await client.startStripeOnboard();
28
+ output.success("Stripe onboarding started.");
29
+ console.log(`\n Open this URL in your browser:\n ${result.url}\n`);
30
+ });
31
+ }
32
+ //# sourceMappingURL=billing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC,MAAM,UAAU,uBAAuB,CACrC,OAAgB,EAChB,SAA0B;IAE1B,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,4BAA4B,CAAC,CAAC;IAE7C,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;QAC9C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,iDAAiD,CAAC;SAC9D,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,yCAAyC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { ApiClient } from "../client.js";
3
+ export declare function registerInitCommand(program: Command, getClient: () => ApiClient): void;
@@ -0,0 +1,42 @@
1
+ import * as output from "../output.js";
2
+ export function registerInitCommand(program, getClient) {
3
+ program
4
+ .command("init <name>")
5
+ .description("Create a new product with a GitHub repo for agent-first configuration")
6
+ .option("--base-url <url>", "Backend API base URL")
7
+ .option("--description <desc>", "Product description")
8
+ .option("--display-name <name>", "Display name")
9
+ .option("--billing <strategy>", "Billing strategy: subscription|usage_based|hybrid", "subscription")
10
+ .action(async (name, opts) => {
11
+ const client = getClient();
12
+ const result = await client.initProduct({
13
+ name,
14
+ baseUrl: opts.baseUrl,
15
+ description: opts.description,
16
+ displayName: opts.displayName,
17
+ billingStrategy: opts.billing,
18
+ });
19
+ const format = output.outputFormat(program.opts().format);
20
+ if (format === "json") {
21
+ console.log(output.json(result));
22
+ return;
23
+ }
24
+ output.success(`Created product "${result.product.name}" (DRAFT)`);
25
+ console.log();
26
+ if (result.repo) {
27
+ console.log(` Repository: ${result.repo.htmlUrl}`);
28
+ console.log(` Clone: git clone ${result.repo.cloneUrl}`);
29
+ console.log();
30
+ }
31
+ console.log(" Next steps:");
32
+ console.log(" 1. Clone the repository");
33
+ console.log(" 2. Read AGENTS.md for the full configuration reference");
34
+ console.log(" 3. Edit product.yaml — add your base URL, plans, and meters");
35
+ console.log(" 4. Push to main — a valid config goes live automatically");
36
+ console.log();
37
+ if (result.agent.agentsMdUrl) {
38
+ console.log(` Docs: ${result.agent.agentsMdUrl}`);
39
+ }
40
+ });
41
+ }
42
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC,MAAM,UAAU,mBAAmB,CACjC,OAAgB,EAChB,SAA0B;IAE1B,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CACV,uEAAuE,CACxE;SACA,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;SAClD,MAAM,CAAC,sBAAsB,EAAE,qBAAqB,CAAC;SACrD,MAAM,CAAC,uBAAuB,EAAE,cAAc,CAAC;SAC/C,MAAM,CACL,sBAAsB,EACtB,mDAAmD,EACnD,cAAc,CACf;SACA,MAAM,CACL,KAAK,EACH,IAAY,EACZ,IAKC,EACD,EAAE;QACF,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;YACtC,IAAI;YACJ,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,eAAe,EAAE,IAAI,CAAC,OAAO;SAC9B,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;QAE1D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,oBAAoB,MAAM,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,EAAE,CAAC;QAEd,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,4BAA4B,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CACT,4DAA4D,CAC7D,CAAC;QACF,OAAO,CAAC,GAAG,CACT,iEAAiE,CAClE,CAAC;QACF,OAAO,CAAC,GAAG,CACT,8DAA8D,CAC/D,CAAC;QACF,OAAO,CAAC,GAAG,EAAE,CAAC;QAEd,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CACF,CAAC;AACN,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerLoginCommands(program: Command): void;
@@ -0,0 +1,82 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { createClient } from "../client.js";
3
+ import { loadConfig, saveCredentials, clearCredentials, loadCredentials, } from "../config.js";
4
+ import { resolveToken } from "../auth.js";
5
+ import * as output from "../output.js";
6
+ export function registerLoginCommands(program) {
7
+ program
8
+ .command("login")
9
+ .description("Authenticate with FartherShore")
10
+ .action(async () => {
11
+ const config = loadConfig();
12
+ console.log("Authenticate with FartherShore\n");
13
+ console.log("1. Create a token at https://farthershore.com/settings/tokens");
14
+ console.log("2. Paste it below:\n");
15
+ const rl = readline.createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout,
18
+ });
19
+ const token = await rl.question("Token: ");
20
+ rl.close();
21
+ if (!token.trim()) {
22
+ output.error("No token provided.");
23
+ process.exit(1);
24
+ }
25
+ try {
26
+ const client = createClient({
27
+ apiUrl: config.apiUrl,
28
+ token: token.trim(),
29
+ });
30
+ const ctx = await client.bootstrap();
31
+ saveCredentials({
32
+ token: token.trim(),
33
+ orgId: ctx.activeOrganization.id,
34
+ userId: ctx.user.id,
35
+ });
36
+ output.success(`Authenticated as user ${ctx.user.id}`);
37
+ console.log(` Organization: ${ctx.activeOrganization.name ?? ctx.activeOrganization.slug ?? ctx.activeOrganization.id}`);
38
+ output.info("Credentials saved to ~/.farthershore/credentials.json");
39
+ }
40
+ catch (err) {
41
+ output.error(err instanceof Error ? err.message : "Authentication failed");
42
+ process.exit(1);
43
+ }
44
+ });
45
+ program
46
+ .command("logout")
47
+ .description("Clear stored credentials")
48
+ .action(() => {
49
+ clearCredentials();
50
+ output.success("Credentials cleared.");
51
+ });
52
+ program
53
+ .command("whoami")
54
+ .description("Show current auth context")
55
+ .action(async () => {
56
+ const config = loadConfig();
57
+ try {
58
+ const token = resolveToken();
59
+ const client = createClient({ apiUrl: config.apiUrl, token });
60
+ const ctx = await client.bootstrap();
61
+ output.heading("Current Context");
62
+ console.log(` User: ${ctx.user.id}`);
63
+ console.log(` Organization: ${ctx.activeOrganization.name ?? ctx.activeOrganization.id}`);
64
+ console.log(` Memberships: ${ctx.memberships.length} org(s)`);
65
+ const creds = loadCredentials();
66
+ if (creds?.token?.startsWith("mk_")) {
67
+ output.info(" Auth: Maker Token");
68
+ }
69
+ else if (process.env.FARTHERSHORE_TOKEN) {
70
+ output.info(" Auth: Environment variable");
71
+ }
72
+ else {
73
+ output.info(" Auth: Stored credentials");
74
+ }
75
+ }
76
+ catch (err) {
77
+ output.error(err instanceof Error ? err.message : "Not authenticated");
78
+ process.exit(1);
79
+ }
80
+ });
81
+ }
82
+ //# 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,3 @@
1
+ import { Command } from "commander";
2
+ import type { ApiClient } from "../client.js";
3
+ export declare function registerPlanCommands(program: Command, getClient: () => ApiClient): void;