@farthershore/cli 0.3.9 → 0.3.11
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 +49 -0
- package/dist/index.js +433 -94
- package/dist/mcp.js +201 -0
- package/package.json +17 -12
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -17
- package/dist/build-info.d.ts +0 -1
- package/dist/build-info.js +0 -10
- package/dist/client.d.ts +0 -89
- package/dist/client.js +0 -82
- package/dist/commands/apply.d.ts +0 -3
- package/dist/commands/apply.js +0 -296
- package/dist/commands/billing.d.ts +0 -3
- package/dist/commands/billing.js +0 -99
- package/dist/commands/feature.d.ts +0 -3
- package/dist/commands/feature.js +0 -109
- package/dist/commands/helpers.d.ts +0 -15
- package/dist/commands/helpers.js +0 -93
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.js +0 -43
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -144
- package/dist/commands/meter.d.ts +0 -3
- package/dist/commands/meter.js +0 -121
- package/dist/commands/plan-transition.d.ts +0 -40
- package/dist/commands/plan-transition.js +0 -504
- package/dist/commands/plan.d.ts +0 -3
- package/dist/commands/plan.js +0 -234
- package/dist/commands/product.d.ts +0 -3
- package/dist/commands/product.js +0 -137
- package/dist/commands/transition.d.ts +0 -3
- package/dist/commands/transition.js +0 -80
- package/dist/commands/validate.d.ts +0 -28
- package/dist/commands/validate.js +0 -216
- package/dist/config.d.ts +0 -6
- package/dist/config.js +0 -58
- package/dist/index.d.ts +0 -2
- package/dist/output.d.ts +0 -8
- package/dist/output.js +0 -28
- package/dist/remediation.d.ts +0 -6
- package/dist/remediation.js +0 -53
- package/dist/types.d.ts +0 -75
- package/dist/types.js +0 -23
package/dist/commands/feature.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { findPlan, loadAcceptedSpec, objectAt, printResult, resolveProductId, stringArrayAt, updateSpec, } from "./helpers.js";
|
|
2
|
-
export function registerFeatureCommands(program, getClient) {
|
|
3
|
-
const feature = program
|
|
4
|
-
.command("feature")
|
|
5
|
-
.description("Manage route-bound product features")
|
|
6
|
-
.addHelpText("after", `
|
|
7
|
-
Agent notes:
|
|
8
|
-
Features gate routes. Add a feature with one or more METHOD:/path routes, then bind it to plans.
|
|
9
|
-
Use METHOD:* only when the feature should cover all methods for a path.
|
|
10
|
-
|
|
11
|
-
Examples:
|
|
12
|
-
farthershore feature add chat_completions --route POST:/v1/chat/completions --format json
|
|
13
|
-
farthershore feature bind chat_completions --plan pro --format json
|
|
14
|
-
farthershore feature list --format json
|
|
15
|
-
`);
|
|
16
|
-
feature
|
|
17
|
-
.command("list")
|
|
18
|
-
.option("--product <product>", "Product id or name")
|
|
19
|
-
.addHelpText("after", `
|
|
20
|
-
Example:
|
|
21
|
-
farthershore feature list --format json
|
|
22
|
-
`)
|
|
23
|
-
.action(async (opts) => {
|
|
24
|
-
const client = getClient();
|
|
25
|
-
const productId = await resolveProductId(client, opts.product);
|
|
26
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
27
|
-
printResult(program, "Features loaded", {
|
|
28
|
-
ok: true,
|
|
29
|
-
productId,
|
|
30
|
-
features: objectAt(spec, "features"),
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
feature
|
|
34
|
-
.command("add <key>")
|
|
35
|
-
.option("--product <product>", "Product id or name")
|
|
36
|
-
.option("--route <route>", "Route binding METHOD:/path; repeatable", collect, [])
|
|
37
|
-
.option("--dry-run", "Validate without mutating")
|
|
38
|
-
.addHelpText("after", `
|
|
39
|
-
Examples:
|
|
40
|
-
farthershore feature add chat_completions --route POST:/v1/chat/completions --format json
|
|
41
|
-
farthershore feature add weather_read --route GET:/v1/weather --route GET:/v1/forecast --dry-run --format json
|
|
42
|
-
`)
|
|
43
|
-
.action(async (key, opts) => {
|
|
44
|
-
const client = getClient();
|
|
45
|
-
const productId = await resolveProductId(client, opts.product);
|
|
46
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
47
|
-
const features = objectAt(spec, "features");
|
|
48
|
-
features[key] = { routes: opts.route.map(parseRoute) };
|
|
49
|
-
await updateSpec(program, client, productId, spec, {
|
|
50
|
-
dryRun: opts.dryRun,
|
|
51
|
-
message: `Feature "${key}" saved`,
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
feature
|
|
55
|
-
.command("bind <key>")
|
|
56
|
-
.requiredOption("--plan <plan>", "Plan key")
|
|
57
|
-
.option("--product <product>", "Product id or name")
|
|
58
|
-
.option("--dry-run", "Validate without mutating")
|
|
59
|
-
.addHelpText("after", `
|
|
60
|
-
Example:
|
|
61
|
-
farthershore feature bind chat_completions --plan pro --format json
|
|
62
|
-
`)
|
|
63
|
-
.action(async (key, opts) => {
|
|
64
|
-
const client = getClient();
|
|
65
|
-
const productId = await resolveProductId(client, opts.product);
|
|
66
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
67
|
-
const plan = findPlan(spec, opts.plan);
|
|
68
|
-
const features = stringArrayAt(plan, "features");
|
|
69
|
-
if (!features.includes(key))
|
|
70
|
-
features.push(key);
|
|
71
|
-
await updateSpec(program, client, productId, spec, {
|
|
72
|
-
dryRun: opts.dryRun,
|
|
73
|
-
message: `Feature "${key}" bound to "${opts.plan}"`,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
feature
|
|
77
|
-
.command("unbind <key>")
|
|
78
|
-
.requiredOption("--plan <plan>", "Plan key")
|
|
79
|
-
.option("--product <product>", "Product id or name")
|
|
80
|
-
.option("--dry-run", "Validate without mutating")
|
|
81
|
-
.addHelpText("after", `
|
|
82
|
-
Example:
|
|
83
|
-
farthershore feature unbind chat_completions --plan starter --format json
|
|
84
|
-
`)
|
|
85
|
-
.action(async (key, opts) => {
|
|
86
|
-
const client = getClient();
|
|
87
|
-
const productId = await resolveProductId(client, opts.product);
|
|
88
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
89
|
-
const plan = findPlan(spec, opts.plan);
|
|
90
|
-
plan.features = stringArrayAt(plan, "features").filter((featureKey) => featureKey !== key);
|
|
91
|
-
await updateSpec(program, client, productId, spec, {
|
|
92
|
-
dryRun: opts.dryRun,
|
|
93
|
-
message: `Feature "${key}" unbound from "${opts.plan}"`,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
function collect(value, previous) {
|
|
98
|
-
previous.push(value);
|
|
99
|
-
return previous;
|
|
100
|
-
}
|
|
101
|
-
function parseRoute(value) {
|
|
102
|
-
const separator = value.indexOf(":");
|
|
103
|
-
if (separator === -1)
|
|
104
|
-
return { method: "*", path: value };
|
|
105
|
-
return {
|
|
106
|
-
method: value.slice(0, separator).toUpperCase(),
|
|
107
|
-
path: value.slice(separator + 1),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
2
|
-
import type { ApiClient } from "../client.js";
|
|
3
|
-
export declare function commandFormat(program: Command): "table" | "json";
|
|
4
|
-
export declare function printResult(program: Command, humanMessage: string, payload: Record<string, unknown>): void;
|
|
5
|
-
export declare function resolveProductId(client: ApiClient, productArg?: string): Promise<string>;
|
|
6
|
-
export declare function resolvePlanId(client: ApiClient, productId: string, planArg: string): Promise<string>;
|
|
7
|
-
export declare function loadAcceptedSpec(client: ApiClient, productId: string): Promise<Record<string, unknown>>;
|
|
8
|
-
export declare function plans(spec: Record<string, unknown>): Array<Record<string, unknown>>;
|
|
9
|
-
export declare function objectAt(parent: Record<string, unknown>, key: string): Record<string, unknown>;
|
|
10
|
-
export declare function stringArrayAt(parent: Record<string, unknown>, key: string): string[];
|
|
11
|
-
export declare function findPlan(spec: Record<string, unknown>, planKey: string): Record<string, unknown>;
|
|
12
|
-
export declare function updateSpec(program: Command, client: ApiClient, productId: string, spec: Record<string, unknown>, opts?: {
|
|
13
|
-
dryRun?: boolean;
|
|
14
|
-
message?: string;
|
|
15
|
-
}): Promise<unknown>;
|
package/dist/commands/helpers.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import * as output from "../output.js";
|
|
3
|
-
export function commandFormat(program) {
|
|
4
|
-
return output.outputFormat(program.opts().format);
|
|
5
|
-
}
|
|
6
|
-
export function printResult(program, humanMessage, payload) {
|
|
7
|
-
if (commandFormat(program) === "json") {
|
|
8
|
-
console.log(output.json(payload));
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
output.success(humanMessage);
|
|
12
|
-
}
|
|
13
|
-
export async function resolveProductId(client, productArg) {
|
|
14
|
-
const config = loadConfig();
|
|
15
|
-
if (productArg) {
|
|
16
|
-
if (productArg.length === 36)
|
|
17
|
-
return productArg;
|
|
18
|
-
const products = client.isMakerToken()
|
|
19
|
-
? await client.managementListProducts()
|
|
20
|
-
: await client.listProducts();
|
|
21
|
-
const product = products.find((p) => p.id === productArg ||
|
|
22
|
-
p.name.toLowerCase() === productArg.toLowerCase());
|
|
23
|
-
if (product)
|
|
24
|
-
return product.id;
|
|
25
|
-
}
|
|
26
|
-
if (config.activeProductId)
|
|
27
|
-
return config.activeProductId;
|
|
28
|
-
throw new Error("No product selected. Pass --product <id-or-name> or run `farthershore product create` first.");
|
|
29
|
-
}
|
|
30
|
-
export async function resolvePlanId(client, productId, planArg) {
|
|
31
|
-
if (planArg.length === 36)
|
|
32
|
-
return planArg;
|
|
33
|
-
const products = client.isMakerToken()
|
|
34
|
-
? await client.managementListProducts()
|
|
35
|
-
: await client.listProducts();
|
|
36
|
-
const product = products.find((p) => p.id === productId);
|
|
37
|
-
const plan = product?.plans?.find((p) => p.id === planArg ||
|
|
38
|
-
p.key === planArg ||
|
|
39
|
-
p.name.toLowerCase() === planArg.toLowerCase());
|
|
40
|
-
if (!plan)
|
|
41
|
-
throw new Error(`Plan "${planArg}" not found`);
|
|
42
|
-
return plan.id;
|
|
43
|
-
}
|
|
44
|
-
export async function loadAcceptedSpec(client, productId) {
|
|
45
|
-
const config = (await client.getProductConfig(productId));
|
|
46
|
-
if (!config.acceptedConfig ||
|
|
47
|
-
typeof config.acceptedConfig !== "object" ||
|
|
48
|
-
Array.isArray(config.acceptedConfig)) {
|
|
49
|
-
throw new Error("Product has no accepted internal config yet.");
|
|
50
|
-
}
|
|
51
|
-
return config.acceptedConfig;
|
|
52
|
-
}
|
|
53
|
-
export function plans(spec) {
|
|
54
|
-
if (!Array.isArray(spec.plans))
|
|
55
|
-
spec.plans = [];
|
|
56
|
-
return spec.plans;
|
|
57
|
-
}
|
|
58
|
-
export function objectAt(parent, key) {
|
|
59
|
-
const current = parent[key];
|
|
60
|
-
if (current && typeof current === "object" && !Array.isArray(current)) {
|
|
61
|
-
return current;
|
|
62
|
-
}
|
|
63
|
-
const next = {};
|
|
64
|
-
parent[key] = next;
|
|
65
|
-
return next;
|
|
66
|
-
}
|
|
67
|
-
export function stringArrayAt(parent, key) {
|
|
68
|
-
if (!Array.isArray(parent[key]))
|
|
69
|
-
parent[key] = [];
|
|
70
|
-
const values = parent[key];
|
|
71
|
-
const strings = values.filter((value) => typeof value === "string");
|
|
72
|
-
if (strings.length !== values.length)
|
|
73
|
-
parent[key] = strings;
|
|
74
|
-
return parent[key];
|
|
75
|
-
}
|
|
76
|
-
export function findPlan(spec, planKey) {
|
|
77
|
-
const plan = plans(spec).find((candidate) => candidate.key === planKey);
|
|
78
|
-
if (!plan)
|
|
79
|
-
throw new Error(`Plan "${planKey}" not found`);
|
|
80
|
-
return plan;
|
|
81
|
-
}
|
|
82
|
-
export async function updateSpec(program, client, productId, spec, opts) {
|
|
83
|
-
const result = await client.updateProductConfig(productId, spec, {
|
|
84
|
-
dryRun: opts?.dryRun,
|
|
85
|
-
});
|
|
86
|
-
printResult(program, opts?.message ?? "Product config updated", {
|
|
87
|
-
ok: true,
|
|
88
|
-
productId,
|
|
89
|
-
dryRun: opts?.dryRun === true,
|
|
90
|
-
...(typeof result === "object" && result ? result : {}),
|
|
91
|
-
});
|
|
92
|
-
return result;
|
|
93
|
-
}
|
package/dist/commands/init.d.ts
DELETED
package/dist/commands/init.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
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
|
-
.action(async (name, opts) => {
|
|
10
|
-
const client = getClient();
|
|
11
|
-
const result = await client.initProduct({
|
|
12
|
-
name,
|
|
13
|
-
baseUrl: opts.baseUrl,
|
|
14
|
-
description: opts.description,
|
|
15
|
-
displayName: opts.displayName,
|
|
16
|
-
});
|
|
17
|
-
// `program.opts()` is typed `OptionValues` (string-indexed any).
|
|
18
|
-
// Narrow at the boundary so `outputFormat`'s `string | undefined`
|
|
19
|
-
// parameter type is satisfied without an unsafe-arg lint.
|
|
20
|
-
const formatOpt = program.opts().format;
|
|
21
|
-
const format = output.outputFormat(formatOpt);
|
|
22
|
-
if (format === "json") {
|
|
23
|
-
console.log(output.json(result));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
output.success(`Created product "${result.product.name}" (DRAFT)`);
|
|
27
|
-
console.log();
|
|
28
|
-
if (result.repo) {
|
|
29
|
-
console.log(` Repository: ${result.repo.htmlUrl}`);
|
|
30
|
-
console.log(` Clone: git clone ${result.repo.cloneUrl}`);
|
|
31
|
-
console.log();
|
|
32
|
-
}
|
|
33
|
-
console.log(" Next steps:");
|
|
34
|
-
console.log(" 1. Clone the repository");
|
|
35
|
-
console.log(" 2. Read AGENTS.md for the full configuration reference");
|
|
36
|
-
console.log(" 3. Edit product.yaml — add your base URL, plans, and meters");
|
|
37
|
-
console.log(" 4. Push to main — a valid config goes live automatically");
|
|
38
|
-
console.log();
|
|
39
|
-
if (result.agent.agentsMdUrl) {
|
|
40
|
-
console.log(` Docs: ${result.agent.agentsMdUrl}`);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
package/dist/commands/login.d.ts
DELETED
package/dist/commands/login.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import * as readline from "node:readline/promises";
|
|
2
|
-
import { createClient } from "../client.js";
|
|
3
|
-
import { loadConfig, saveConfig, saveCredentials, clearCredentials, } from "../config.js";
|
|
4
|
-
import { resolveToken } from "../auth.js";
|
|
5
|
-
import * as output from "../output.js";
|
|
6
|
-
export function registerAuthCommands(program) {
|
|
7
|
-
const auth = program
|
|
8
|
-
.command("auth")
|
|
9
|
-
.description("Manage authentication")
|
|
10
|
-
.addHelpText("after", `
|
|
11
|
-
Agent notes:
|
|
12
|
-
Prefer FARTHERSHORE_TOKEN in CI/agent environments. Use auth set-key for local persistent credentials.
|
|
13
|
-
|
|
14
|
-
Examples:
|
|
15
|
-
farthershore auth set-key "$FARTHERSHORE_TOKEN"
|
|
16
|
-
farthershore whoami --format json
|
|
17
|
-
`);
|
|
18
|
-
auth
|
|
19
|
-
.command("set-key [token]")
|
|
20
|
-
.description("Set your API token")
|
|
21
|
-
.addHelpText("after", `
|
|
22
|
-
Examples:
|
|
23
|
-
farthershore auth set-key mk_xxx
|
|
24
|
-
farthershore auth set-key "$FARTHERSHORE_TOKEN"
|
|
25
|
-
`)
|
|
26
|
-
.action(async (tokenArg) => {
|
|
27
|
-
await setKey(tokenArg);
|
|
28
|
-
});
|
|
29
|
-
program
|
|
30
|
-
.command("set-key [token]")
|
|
31
|
-
.description("Set your API token (interactive or pass as argument)")
|
|
32
|
-
.action(async (tokenArg) => {
|
|
33
|
-
await setKey(tokenArg);
|
|
34
|
-
});
|
|
35
|
-
program
|
|
36
|
-
.command("logout")
|
|
37
|
-
.description("Clear stored credentials")
|
|
38
|
-
.action(() => {
|
|
39
|
-
clearCredentials();
|
|
40
|
-
output.success("Credentials cleared.");
|
|
41
|
-
});
|
|
42
|
-
program
|
|
43
|
-
.command("whoami")
|
|
44
|
-
.description("Show current authentication context")
|
|
45
|
-
.action(async () => {
|
|
46
|
-
const config = loadConfig();
|
|
47
|
-
const formatOpt = program.opts().format;
|
|
48
|
-
const format = output.outputFormat(formatOpt);
|
|
49
|
-
try {
|
|
50
|
-
const token = resolveToken();
|
|
51
|
-
const client = createClient({ apiUrl: config.apiUrl, token });
|
|
52
|
-
const ctx = await client.bootstrap();
|
|
53
|
-
const authSource = process.env.FARTHERSHORE_TOKEN
|
|
54
|
-
? "env:FARTHERSHORE_TOKEN"
|
|
55
|
-
: "credentials-file";
|
|
56
|
-
if (format === "json") {
|
|
57
|
-
// Machine-readable shape — stable contract for CI scripts
|
|
58
|
-
// ("am I logged in?"). Don't include the token itself.
|
|
59
|
-
console.log(output.json({
|
|
60
|
-
organization: {
|
|
61
|
-
id: ctx.activeOrganization.id,
|
|
62
|
-
name: ctx.activeOrganization.name ?? null,
|
|
63
|
-
slug: ctx.activeOrganization.slug ?? null,
|
|
64
|
-
},
|
|
65
|
-
user: { id: ctx.user.id },
|
|
66
|
-
apiUrl: config.apiUrl,
|
|
67
|
-
authSource,
|
|
68
|
-
}));
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
output.heading("Current Context");
|
|
72
|
-
console.log(` Organization: ${ctx.activeOrganization.name ?? ctx.activeOrganization.id}`);
|
|
73
|
-
console.log(` API URL: ${config.apiUrl}`);
|
|
74
|
-
if (authSource === "env:FARTHERSHORE_TOKEN") {
|
|
75
|
-
output.info(" Auth: FARTHERSHORE_TOKEN env var");
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
output.info(" Auth: ~/.farthershore/credentials.json");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
if (format === "json") {
|
|
83
|
-
console.log(output.json({ authenticated: false }));
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
output.error("Not authenticated. Run `farthershore set-key` or set FARTHERSHORE_TOKEN.");
|
|
87
|
-
}
|
|
88
|
-
process.exitCode = 1;
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
program
|
|
92
|
-
.command("set-url <url>")
|
|
93
|
-
.description("Override the API base URL (for staging/testing)")
|
|
94
|
-
.action((url) => {
|
|
95
|
-
saveConfig({ apiUrl: url });
|
|
96
|
-
output.success(`API URL set to ${url}`);
|
|
97
|
-
});
|
|
98
|
-
program
|
|
99
|
-
.command("reset-url")
|
|
100
|
-
.description("Reset the API URL to production default")
|
|
101
|
-
.action(() => {
|
|
102
|
-
saveConfig({ apiUrl: "https://core.farthershore.com" });
|
|
103
|
-
output.success("API URL reset to https://core.farthershore.com");
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
async function setKey(tokenArg) {
|
|
107
|
-
let token = tokenArg?.trim();
|
|
108
|
-
if (!token) {
|
|
109
|
-
if (!process.stdin.isTTY) {
|
|
110
|
-
output.error("No token provided. Pass it as an argument: farthershore auth set-key <token>");
|
|
111
|
-
process.exitCode = 1;
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
console.log("Set your FartherShore API token\n");
|
|
115
|
-
console.log(" Create a token at https://farthershore.com/settings/tokens\n");
|
|
116
|
-
const rl = readline.createInterface({
|
|
117
|
-
input: process.stdin,
|
|
118
|
-
output: process.stdout,
|
|
119
|
-
});
|
|
120
|
-
token = (await rl.question("Token: ")).trim();
|
|
121
|
-
rl.close();
|
|
122
|
-
}
|
|
123
|
-
if (!token) {
|
|
124
|
-
output.error("No token provided.");
|
|
125
|
-
process.exitCode = 1;
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const config = loadConfig();
|
|
129
|
-
try {
|
|
130
|
-
const client = createClient({ apiUrl: config.apiUrl, token });
|
|
131
|
-
const ctx = await client.bootstrap();
|
|
132
|
-
saveCredentials({
|
|
133
|
-
token,
|
|
134
|
-
orgId: ctx.activeOrganization.id,
|
|
135
|
-
userId: ctx.user.id,
|
|
136
|
-
});
|
|
137
|
-
output.success("Authenticated");
|
|
138
|
-
console.log(` Organization: ${ctx.activeOrganization.name ?? ctx.activeOrganization.id}`);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
output.error("Invalid token. Check it and try again.");
|
|
142
|
-
process.exitCode = 1;
|
|
143
|
-
}
|
|
144
|
-
}
|
package/dist/commands/meter.d.ts
DELETED
package/dist/commands/meter.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { loadAcceptedSpec, objectAt, printResult, resolveProductId, updateSpec, } from "./helpers.js";
|
|
2
|
-
export function registerMeterCommands(program, getClient) {
|
|
3
|
-
const meter = program
|
|
4
|
-
.command("meter")
|
|
5
|
-
.description("Manage product usage meters and rating sources")
|
|
6
|
-
.addHelpText("after", `
|
|
7
|
-
Agent notes:
|
|
8
|
-
Meters define what usage is measured. Plans reference meters; plans do not define provider-specific rates.
|
|
9
|
-
Rating sources: fixed, provider_catalog, upstream_reported, external_rate_api, custom.
|
|
10
|
-
|
|
11
|
-
Examples:
|
|
12
|
-
farthershore meter add ai_tokens --selector model --measure input_tokens --measure output_tokens --rating provider_catalog --catalog llm_models --format json
|
|
13
|
-
farthershore meter add requests --measure count --rating fixed --format json
|
|
14
|
-
farthershore meter list --format json
|
|
15
|
-
`);
|
|
16
|
-
meter
|
|
17
|
-
.command("list")
|
|
18
|
-
.option("--product <product>", "Product id or name")
|
|
19
|
-
.addHelpText("after", `
|
|
20
|
-
Example:
|
|
21
|
-
farthershore meter list --format json
|
|
22
|
-
`)
|
|
23
|
-
.action(async (opts) => {
|
|
24
|
-
const client = getClient();
|
|
25
|
-
const productId = await resolveProductId(client, opts.product);
|
|
26
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
27
|
-
const usage = objectAt(spec, "usage");
|
|
28
|
-
printResult(program, "Meters loaded", {
|
|
29
|
-
ok: true,
|
|
30
|
-
productId,
|
|
31
|
-
meters: objectAt(usage, "meters"),
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
meter
|
|
35
|
-
.command("add <key>")
|
|
36
|
-
.option("--product <product>", "Product id or name")
|
|
37
|
-
.option("--selector <field>", "Selector field, for example model")
|
|
38
|
-
.option("--measure <measure>", "Measure name; repeatable", collect, [])
|
|
39
|
-
.option("--rating <source>", "Rating source: fixed, provider_catalog, upstream_reported, external_rate_api, custom", "fixed")
|
|
40
|
-
.option("--catalog <catalog>", "Catalog key for provider_catalog rating")
|
|
41
|
-
.option("--resolver <resolver>", "Resolver id for external/custom rating")
|
|
42
|
-
.option("--amount-field <path>", "Amount field for upstream_reported rating")
|
|
43
|
-
.option("--currency-field <path>", "Currency field for upstream_reported rating")
|
|
44
|
-
.option("--dry-run", "Validate without mutating")
|
|
45
|
-
.addHelpText("after", `
|
|
46
|
-
Examples:
|
|
47
|
-
farthershore meter add ai_tokens --selector model --measure input_tokens --measure output_tokens --rating provider_catalog --catalog llm_models --format json
|
|
48
|
-
farthershore meter add requests --measure count --rating fixed --dry-run --format json
|
|
49
|
-
farthershore meter add upstream_cost --measure cost_micros --rating upstream_reported --amount-field usage.cost_micros --format json
|
|
50
|
-
`)
|
|
51
|
-
.action(async (key, opts) => {
|
|
52
|
-
const client = getClient();
|
|
53
|
-
const productId = await resolveProductId(client, opts.product);
|
|
54
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
55
|
-
const usage = objectAt(spec, "usage");
|
|
56
|
-
const meters = objectAt(usage, "meters");
|
|
57
|
-
meters[key] = {
|
|
58
|
-
...(opts.selector ? { selector: opts.selector } : {}),
|
|
59
|
-
measures: opts.measure.length > 0 ? opts.measure : ["count"],
|
|
60
|
-
rating: ratingFromOptions(opts),
|
|
61
|
-
};
|
|
62
|
-
await updateSpec(program, client, productId, spec, {
|
|
63
|
-
dryRun: opts.dryRun,
|
|
64
|
-
message: `Meter "${key}" saved`,
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
meter
|
|
68
|
-
.command("rating set <key>")
|
|
69
|
-
.option("--product <product>", "Product id or name")
|
|
70
|
-
.requiredOption("--rating <source>", "Rating source: fixed, provider_catalog, upstream_reported, external_rate_api, custom")
|
|
71
|
-
.option("--catalog <catalog>", "Catalog key for provider_catalog rating")
|
|
72
|
-
.option("--resolver <resolver>", "Resolver id for external/custom rating")
|
|
73
|
-
.option("--amount-field <path>", "Amount field for upstream_reported rating")
|
|
74
|
-
.option("--currency-field <path>", "Currency field for upstream_reported rating")
|
|
75
|
-
.option("--dry-run", "Validate without mutating")
|
|
76
|
-
.addHelpText("after", `
|
|
77
|
-
Examples:
|
|
78
|
-
farthershore meter rating set ai_tokens --rating provider_catalog --catalog llm_models --format json
|
|
79
|
-
farthershore meter rating set upstream_cost --rating upstream_reported --amount-field usage.cost_micros --dry-run --format json
|
|
80
|
-
`)
|
|
81
|
-
.action(async (key, opts) => {
|
|
82
|
-
const client = getClient();
|
|
83
|
-
const productId = await resolveProductId(client, opts.product);
|
|
84
|
-
const spec = await loadAcceptedSpec(client, productId);
|
|
85
|
-
const usage = objectAt(spec, "usage");
|
|
86
|
-
const meters = objectAt(usage, "meters");
|
|
87
|
-
const current = meters[key];
|
|
88
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
89
|
-
throw new Error(`Meter "${key}" not found`);
|
|
90
|
-
}
|
|
91
|
-
current.rating = ratingFromOptions(opts);
|
|
92
|
-
await updateSpec(program, client, productId, spec, {
|
|
93
|
-
dryRun: opts.dryRun,
|
|
94
|
-
message: `Meter "${key}" rating saved`,
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function collect(value, previous) {
|
|
99
|
-
previous.push(value);
|
|
100
|
-
return previous;
|
|
101
|
-
}
|
|
102
|
-
function ratingFromOptions(opts) {
|
|
103
|
-
if (opts.rating === "provider_catalog") {
|
|
104
|
-
return {
|
|
105
|
-
source: opts.rating,
|
|
106
|
-
catalog: opts.catalog ?? "default",
|
|
107
|
-
pricePolicy: "pass_through",
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (opts.rating === "upstream_reported") {
|
|
111
|
-
return {
|
|
112
|
-
source: opts.rating,
|
|
113
|
-
amountField: opts.amountField ?? "usage.cost_micros",
|
|
114
|
-
...(opts.currencyField ? { currencyField: opts.currencyField } : {}),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
if (opts.rating === "external_rate_api" || opts.rating === "custom") {
|
|
118
|
-
return { source: opts.rating, resolver: opts.resolver ?? "default" };
|
|
119
|
-
}
|
|
120
|
-
return { source: "fixed", rates: {} };
|
|
121
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
type ChangeKind = "strategy_changed" | "price_increase" | "price_decrease" | "feature_added" | "feature_removed" | "limit_increased" | "limit_reduced" | "credit_increased" | "credit_reduced" | "rating_changed" | "plan_added" | "plan_removed";
|
|
3
|
-
type SubscriberAction = "preserve_current_period" | "switch_immediately" | "switch_immediately_prorate" | "new_subscribers_only";
|
|
4
|
-
type TransitionChange = {
|
|
5
|
-
kind: ChangeKind;
|
|
6
|
-
planKey?: string;
|
|
7
|
-
meter?: string;
|
|
8
|
-
feature?: string;
|
|
9
|
-
from?: unknown;
|
|
10
|
-
to?: unknown;
|
|
11
|
-
subscriberAction: SubscriberAction;
|
|
12
|
-
requiresAcknowledgment: boolean;
|
|
13
|
-
message: string;
|
|
14
|
-
};
|
|
15
|
-
type TransitionAnalysis = {
|
|
16
|
-
valid: boolean;
|
|
17
|
-
from: string;
|
|
18
|
-
to: string;
|
|
19
|
-
strategy: {
|
|
20
|
-
from?: string;
|
|
21
|
-
to?: string;
|
|
22
|
-
};
|
|
23
|
-
policy: {
|
|
24
|
-
default: SubscriberAction;
|
|
25
|
-
proration: string;
|
|
26
|
-
when: Record<string, SubscriberAction>;
|
|
27
|
-
};
|
|
28
|
-
changes: TransitionChange[];
|
|
29
|
-
errors: string[];
|
|
30
|
-
warnings: string[];
|
|
31
|
-
agentHints: string[];
|
|
32
|
-
};
|
|
33
|
-
export declare function analyzePlanTransition(options: {
|
|
34
|
-
fromSpec: unknown;
|
|
35
|
-
toSpec: unknown;
|
|
36
|
-
fromLabel?: string;
|
|
37
|
-
toLabel?: string;
|
|
38
|
-
}): TransitionAnalysis;
|
|
39
|
-
export declare function registerPlanTransitionCommand(program: Command): void;
|
|
40
|
-
export {};
|