@farthershore/cli 0.3.8 → 0.3.9
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/dist/commands/apply.js +11 -0
- package/dist/commands/billing.js +46 -6
- package/dist/commands/feature.js +30 -1
- package/dist/commands/login.js +16 -1
- package/dist/commands/meter.js +30 -3
- package/dist/commands/plan.js +50 -1
- package/dist/commands/product.js +40 -4
- package/dist/commands/transition.js +18 -1
- package/dist/commands/validate.js +11 -1
- package/dist/index.js +29 -2
- package/package.json +1 -1
package/dist/commands/apply.js
CHANGED
|
@@ -223,6 +223,17 @@ export function registerApplyCommand(program, getClient) {
|
|
|
223
223
|
"Automatically scopes to the current git branch so env branches compile against their own plans.")
|
|
224
224
|
.option("--branch <branch>", "Override the branch used for env-scoped compilation (default: auto-detected)")
|
|
225
225
|
.option("--dry-run", "Run local validation and remote compile without applying product state")
|
|
226
|
+
.addHelpText("after", `
|
|
227
|
+
Agent notes:
|
|
228
|
+
apply first validates local product.yaml when it matches the target product, then asks core to compile the pushed branch state.
|
|
229
|
+
Unpushed local edits are not included in remote compile. Push or use CLI config commands for API-backed changes.
|
|
230
|
+
Use --format json for stable output: ok, success, errors, warnings, nextActions.
|
|
231
|
+
|
|
232
|
+
Examples:
|
|
233
|
+
farthershore apply --dry-run --format json
|
|
234
|
+
farthershore apply my-weather-api --dry-run --format json
|
|
235
|
+
farthershore apply my-weather-api --branch feature/billing-change --dry-run --format json
|
|
236
|
+
`)
|
|
226
237
|
.action(async (productArg, opts) => {
|
|
227
238
|
const client = getClient();
|
|
228
239
|
const globalFormat = program.opts().format;
|
package/dist/commands/billing.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import { loadAcceptedSpec, objectAt, printResult, resolveProductId, updateSpec, } from "./helpers.js";
|
|
2
2
|
export function registerBillingCommands(program, getClient) {
|
|
3
|
-
const billing = program
|
|
4
|
-
|
|
3
|
+
const billing = program
|
|
4
|
+
.command("billing")
|
|
5
|
+
.description("Manage product-level billing strategy and transition policy")
|
|
6
|
+
.addHelpText("after", `
|
|
7
|
+
Agent notes:
|
|
8
|
+
Billing strategy is product-level for paid plans. Paid plans should not mix strategies.
|
|
9
|
+
Strategies: free_trial, flat_subscription, included_usage, subscription_overage, pay_as_you_go, prepaid_credits.
|
|
10
|
+
Subscriber actions: preserve_current_period, switch_immediately, switch_immediately_prorate, new_subscribers_only.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
farthershore billing strategy get --format json
|
|
14
|
+
farthershore billing strategy set prepaid_credits --transition preserve_current_period --format json
|
|
15
|
+
farthershore billing policy set --default preserve_current_period --proration none --format json
|
|
16
|
+
`);
|
|
17
|
+
const strategy = billing
|
|
18
|
+
.command("strategy")
|
|
19
|
+
.description("Get or set the product-level billing strategy")
|
|
20
|
+
.addHelpText("after", `
|
|
21
|
+
Examples:
|
|
22
|
+
farthershore billing strategy get --format json
|
|
23
|
+
farthershore billing strategy set subscription_overage --format json
|
|
24
|
+
`);
|
|
5
25
|
strategy
|
|
6
26
|
.command("get")
|
|
7
27
|
.option("--product <product>", "Product id or name")
|
|
28
|
+
.addHelpText("after", `
|
|
29
|
+
Example:
|
|
30
|
+
farthershore billing strategy get --format json
|
|
31
|
+
`)
|
|
8
32
|
.action(async (opts) => {
|
|
9
33
|
const client = getClient();
|
|
10
34
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -18,9 +42,14 @@ export function registerBillingCommands(program, getClient) {
|
|
|
18
42
|
strategy
|
|
19
43
|
.command("set <strategy>")
|
|
20
44
|
.option("--product <product>", "Product id or name")
|
|
21
|
-
.option("--transition <action>", "Default subscriber transition action", "preserve_current_period")
|
|
45
|
+
.option("--transition <action>", "Default subscriber transition action: preserve_current_period, switch_immediately, switch_immediately_prorate, new_subscribers_only", "preserve_current_period")
|
|
22
46
|
.option("--proration <mode>", "Proration mode", "none")
|
|
23
47
|
.option("--dry-run", "Validate without mutating")
|
|
48
|
+
.addHelpText("after", `
|
|
49
|
+
Examples:
|
|
50
|
+
farthershore billing strategy set prepaid_credits --transition preserve_current_period --format json
|
|
51
|
+
farthershore billing strategy set pay_as_you_go --dry-run --format json
|
|
52
|
+
`)
|
|
24
53
|
.action(async (nextStrategy, opts) => {
|
|
25
54
|
const client = getClient();
|
|
26
55
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -36,12 +65,23 @@ export function registerBillingCommands(program, getClient) {
|
|
|
36
65
|
message: "Billing strategy updated",
|
|
37
66
|
});
|
|
38
67
|
});
|
|
39
|
-
billing
|
|
40
|
-
.command("policy
|
|
68
|
+
const policy = billing
|
|
69
|
+
.command("policy")
|
|
70
|
+
.description("Set subscriber change policy defaults")
|
|
71
|
+
.addHelpText("after", `
|
|
72
|
+
Example:
|
|
73
|
+
farthershore billing policy set --default preserve_current_period --proration none --format json
|
|
74
|
+
`);
|
|
75
|
+
policy
|
|
76
|
+
.command("set")
|
|
41
77
|
.option("--product <product>", "Product id or name")
|
|
42
|
-
.option("--default <action>", "Default subscriber action", "preserve_current_period")
|
|
78
|
+
.option("--default <action>", "Default subscriber action: preserve_current_period, switch_immediately, switch_immediately_prorate, new_subscribers_only", "preserve_current_period")
|
|
43
79
|
.option("--proration <mode>", "Proration mode", "none")
|
|
44
80
|
.option("--dry-run", "Validate without mutating")
|
|
81
|
+
.addHelpText("after", `
|
|
82
|
+
Example:
|
|
83
|
+
farthershore billing policy set --default preserve_current_period --proration none --format json
|
|
84
|
+
`)
|
|
45
85
|
.action(async (opts) => {
|
|
46
86
|
const client = getClient();
|
|
47
87
|
const productId = await resolveProductId(client, opts.product);
|
package/dist/commands/feature.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { findPlan, loadAcceptedSpec, objectAt, printResult, resolveProductId, stringArrayAt, updateSpec, } from "./helpers.js";
|
|
2
2
|
export function registerFeatureCommands(program, getClient) {
|
|
3
|
-
const feature = program
|
|
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
|
+
`);
|
|
4
16
|
feature
|
|
5
17
|
.command("list")
|
|
6
18
|
.option("--product <product>", "Product id or name")
|
|
19
|
+
.addHelpText("after", `
|
|
20
|
+
Example:
|
|
21
|
+
farthershore feature list --format json
|
|
22
|
+
`)
|
|
7
23
|
.action(async (opts) => {
|
|
8
24
|
const client = getClient();
|
|
9
25
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -19,6 +35,11 @@ export function registerFeatureCommands(program, getClient) {
|
|
|
19
35
|
.option("--product <product>", "Product id or name")
|
|
20
36
|
.option("--route <route>", "Route binding METHOD:/path; repeatable", collect, [])
|
|
21
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
|
+
`)
|
|
22
43
|
.action(async (key, opts) => {
|
|
23
44
|
const client = getClient();
|
|
24
45
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -35,6 +56,10 @@ export function registerFeatureCommands(program, getClient) {
|
|
|
35
56
|
.requiredOption("--plan <plan>", "Plan key")
|
|
36
57
|
.option("--product <product>", "Product id or name")
|
|
37
58
|
.option("--dry-run", "Validate without mutating")
|
|
59
|
+
.addHelpText("after", `
|
|
60
|
+
Example:
|
|
61
|
+
farthershore feature bind chat_completions --plan pro --format json
|
|
62
|
+
`)
|
|
38
63
|
.action(async (key, opts) => {
|
|
39
64
|
const client = getClient();
|
|
40
65
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -53,6 +78,10 @@ export function registerFeatureCommands(program, getClient) {
|
|
|
53
78
|
.requiredOption("--plan <plan>", "Plan key")
|
|
54
79
|
.option("--product <product>", "Product id or name")
|
|
55
80
|
.option("--dry-run", "Validate without mutating")
|
|
81
|
+
.addHelpText("after", `
|
|
82
|
+
Example:
|
|
83
|
+
farthershore feature unbind chat_completions --plan starter --format json
|
|
84
|
+
`)
|
|
56
85
|
.action(async (key, opts) => {
|
|
57
86
|
const client = getClient();
|
|
58
87
|
const productId = await resolveProductId(client, opts.product);
|
package/dist/commands/login.js
CHANGED
|
@@ -4,10 +4,25 @@ import { loadConfig, saveConfig, saveCredentials, clearCredentials, } from "../c
|
|
|
4
4
|
import { resolveToken } from "../auth.js";
|
|
5
5
|
import * as output from "../output.js";
|
|
6
6
|
export function registerAuthCommands(program) {
|
|
7
|
-
const auth = 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
|
+
`);
|
|
8
18
|
auth
|
|
9
19
|
.command("set-key [token]")
|
|
10
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
|
+
`)
|
|
11
26
|
.action(async (tokenArg) => {
|
|
12
27
|
await setKey(tokenArg);
|
|
13
28
|
});
|
package/dist/commands/meter.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { loadAcceptedSpec, objectAt, printResult, resolveProductId, updateSpec, } from "./helpers.js";
|
|
2
2
|
export function registerMeterCommands(program, getClient) {
|
|
3
|
-
const meter = program
|
|
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
|
+
`);
|
|
4
16
|
meter
|
|
5
17
|
.command("list")
|
|
6
18
|
.option("--product <product>", "Product id or name")
|
|
19
|
+
.addHelpText("after", `
|
|
20
|
+
Example:
|
|
21
|
+
farthershore meter list --format json
|
|
22
|
+
`)
|
|
7
23
|
.action(async (opts) => {
|
|
8
24
|
const client = getClient();
|
|
9
25
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -20,12 +36,18 @@ export function registerMeterCommands(program, getClient) {
|
|
|
20
36
|
.option("--product <product>", "Product id or name")
|
|
21
37
|
.option("--selector <field>", "Selector field, for example model")
|
|
22
38
|
.option("--measure <measure>", "Measure name; repeatable", collect, [])
|
|
23
|
-
.option("--rating <source>", "Rating source", "fixed")
|
|
39
|
+
.option("--rating <source>", "Rating source: fixed, provider_catalog, upstream_reported, external_rate_api, custom", "fixed")
|
|
24
40
|
.option("--catalog <catalog>", "Catalog key for provider_catalog rating")
|
|
25
41
|
.option("--resolver <resolver>", "Resolver id for external/custom rating")
|
|
26
42
|
.option("--amount-field <path>", "Amount field for upstream_reported rating")
|
|
27
43
|
.option("--currency-field <path>", "Currency field for upstream_reported rating")
|
|
28
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
|
+
`)
|
|
29
51
|
.action(async (key, opts) => {
|
|
30
52
|
const client = getClient();
|
|
31
53
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -45,12 +67,17 @@ export function registerMeterCommands(program, getClient) {
|
|
|
45
67
|
meter
|
|
46
68
|
.command("rating set <key>")
|
|
47
69
|
.option("--product <product>", "Product id or name")
|
|
48
|
-
.requiredOption("--rating <source>", "Rating source")
|
|
70
|
+
.requiredOption("--rating <source>", "Rating source: fixed, provider_catalog, upstream_reported, external_rate_api, custom")
|
|
49
71
|
.option("--catalog <catalog>", "Catalog key for provider_catalog rating")
|
|
50
72
|
.option("--resolver <resolver>", "Resolver id for external/custom rating")
|
|
51
73
|
.option("--amount-field <path>", "Amount field for upstream_reported rating")
|
|
52
74
|
.option("--currency-field <path>", "Currency field for upstream_reported rating")
|
|
53
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
|
+
`)
|
|
54
81
|
.action(async (key, opts) => {
|
|
55
82
|
const client = getClient();
|
|
56
83
|
const productId = await resolveProductId(client, opts.product);
|
package/dist/commands/plan.js
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import { findPlan, loadAcceptedSpec, plans, printResult, resolveProductId, stringArrayAt, updateSpec, } from "./helpers.js";
|
|
2
2
|
export function registerPlanCommands(program, getClient) {
|
|
3
|
-
const plan = program
|
|
3
|
+
const plan = program
|
|
4
|
+
.command("plan")
|
|
5
|
+
.description("Manage free and paid plans in the accepted product config")
|
|
6
|
+
.addHelpText("after", `
|
|
7
|
+
Agent notes:
|
|
8
|
+
Plans are keyed by plan name/key, not by usage type.
|
|
9
|
+
Only one free plan is allowed. Free plans should include a hard enforced limit.
|
|
10
|
+
Duplicate paid prices are rejected server-side. Use --dry-run before risky changes.
|
|
11
|
+
Limit format is meter:window:capacity:enforcement, for example ai_tokens:month:100000:enforce.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
farthershore plan add free --free --limit ai_tokens:month:100000:enforce --feature chat_completions --format json
|
|
15
|
+
farthershore plan add pro --price-monthly 2900 --credits-monthly-micros 500000000 --meter ai_tokens --feature chat_completions --format json
|
|
16
|
+
farthershore plan list --format json
|
|
17
|
+
`);
|
|
4
18
|
plan
|
|
5
19
|
.command("list")
|
|
6
20
|
.option("--product <product>", "Product id or name")
|
|
21
|
+
.addHelpText("after", `
|
|
22
|
+
Example:
|
|
23
|
+
farthershore plan list --format json
|
|
24
|
+
`)
|
|
7
25
|
.action(async (opts) => {
|
|
8
26
|
const client = getClient();
|
|
9
27
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -25,6 +43,12 @@ export function registerPlanCommands(program, getClient) {
|
|
|
25
43
|
.option("--feature <feature>", "Enabled feature; repeatable", collect, [])
|
|
26
44
|
.option("--limit <limit>", "Limit meter:window:capacity:enforcement; repeatable", collect, [])
|
|
27
45
|
.option("--dry-run", "Validate without mutating")
|
|
46
|
+
.addHelpText("after", `
|
|
47
|
+
Examples:
|
|
48
|
+
farthershore plan add free --free --limit ai_tokens:month:100000:enforce --feature chat_completions --format json
|
|
49
|
+
farthershore plan add pro --price-monthly 2900 --credits-monthly-micros 500000000 --meter ai_tokens --feature chat_completions --format json
|
|
50
|
+
farthershore plan add starter --price-monthly 900 --meter requests --feature weather_read --dry-run --format json
|
|
51
|
+
`)
|
|
28
52
|
.action(async (key, opts) => {
|
|
29
53
|
const client = getClient();
|
|
30
54
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -47,6 +71,10 @@ export function registerPlanCommands(program, getClient) {
|
|
|
47
71
|
.requiredOption("--monthly <cents>", "Monthly price in cents", parseInteger)
|
|
48
72
|
.option("--currency <currency>", "Currency", "USD")
|
|
49
73
|
.option("--dry-run", "Validate without mutating")
|
|
74
|
+
.addHelpText("after", `
|
|
75
|
+
Example:
|
|
76
|
+
farthershore plan set-price pro --monthly 4900 --dry-run --format json
|
|
77
|
+
`)
|
|
50
78
|
.action(async (key, opts) => {
|
|
51
79
|
await mutatePlan(program, getClient(), opts.product, key, opts.dryRun, (plan) => {
|
|
52
80
|
plan.free = false;
|
|
@@ -63,6 +91,11 @@ export function registerPlanCommands(program, getClient) {
|
|
|
63
91
|
.option("--product <product>", "Product id or name")
|
|
64
92
|
.requiredOption("--limit <limit>", "Limit meter:window:capacity:enforcement")
|
|
65
93
|
.option("--dry-run", "Validate without mutating")
|
|
94
|
+
.addHelpText("after", `
|
|
95
|
+
Examples:
|
|
96
|
+
farthershore plan set-limit free --limit ai_tokens:month:100000:enforce --format json
|
|
97
|
+
farthershore plan set-limit pro --limit ai_tokens:month:1000000:track --dry-run --format json
|
|
98
|
+
`)
|
|
66
99
|
.action(async (key, opts) => {
|
|
67
100
|
await mutatePlan(program, getClient(), opts.product, key, opts.dryRun, (plan) => {
|
|
68
101
|
const next = parseLimit(opts.limit);
|
|
@@ -82,6 +115,10 @@ export function registerPlanCommands(program, getClient) {
|
|
|
82
115
|
.option("--product <product>", "Product id or name")
|
|
83
116
|
.requiredOption("--monthly-micros <micros>", "Monthly included credits", parseInteger)
|
|
84
117
|
.option("--dry-run", "Validate without mutating")
|
|
118
|
+
.addHelpText("after", `
|
|
119
|
+
Example:
|
|
120
|
+
farthershore plan set-credits pro --monthly-micros 500000000 --format json
|
|
121
|
+
`)
|
|
85
122
|
.action(async (key, opts) => {
|
|
86
123
|
await mutatePlan(program, getClient(), opts.product, key, opts.dryRun, (plan) => {
|
|
87
124
|
plan.credits = { monthlyIncludedMicros: opts.monthlyMicros };
|
|
@@ -92,6 +129,10 @@ export function registerPlanCommands(program, getClient) {
|
|
|
92
129
|
.argument("<feature>")
|
|
93
130
|
.option("--product <product>", "Product id or name")
|
|
94
131
|
.option("--dry-run", "Validate without mutating")
|
|
132
|
+
.addHelpText("after", `
|
|
133
|
+
Example:
|
|
134
|
+
farthershore plan add-feature pro chat_completions --format json
|
|
135
|
+
`)
|
|
95
136
|
.action(async (key, feature, opts) => {
|
|
96
137
|
await mutatePlan(program, getClient(), opts.product, key, opts.dryRun, (plan) => {
|
|
97
138
|
const features = stringArrayAt(plan, "features");
|
|
@@ -104,6 +145,10 @@ export function registerPlanCommands(program, getClient) {
|
|
|
104
145
|
.argument("<feature>")
|
|
105
146
|
.option("--product <product>", "Product id or name")
|
|
106
147
|
.option("--dry-run", "Validate without mutating")
|
|
148
|
+
.addHelpText("after", `
|
|
149
|
+
Example:
|
|
150
|
+
farthershore plan remove-feature starter chat_completions --dry-run --format json
|
|
151
|
+
`)
|
|
107
152
|
.action(async (key, feature, opts) => {
|
|
108
153
|
await mutatePlan(program, getClient(), opts.product, key, opts.dryRun, (plan) => {
|
|
109
154
|
plan.features = stringArrayAt(plan, "features").filter((candidate) => candidate !== feature);
|
|
@@ -113,6 +158,10 @@ export function registerPlanCommands(program, getClient) {
|
|
|
113
158
|
.command("remove <key>")
|
|
114
159
|
.option("--product <product>", "Product id or name")
|
|
115
160
|
.option("--dry-run", "Validate without mutating")
|
|
161
|
+
.addHelpText("after", `
|
|
162
|
+
Example:
|
|
163
|
+
farthershore plan remove starter --dry-run --format json
|
|
164
|
+
`)
|
|
116
165
|
.action(async (key, opts) => {
|
|
117
166
|
const client = getClient();
|
|
118
167
|
const productId = await resolveProductId(client, opts.product);
|
package/dist/commands/product.js
CHANGED
|
@@ -2,15 +2,38 @@ import { saveConfig } from "../config.js";
|
|
|
2
2
|
import * as output from "../output.js";
|
|
3
3
|
import { commandFormat, printResult, resolveProductId } from "./helpers.js";
|
|
4
4
|
export function registerProductCommands(program, getClient) {
|
|
5
|
-
const product = program
|
|
5
|
+
const product = program
|
|
6
|
+
.command("product")
|
|
7
|
+
.description("Manage products and inspect the latest accepted internal product config")
|
|
8
|
+
.addHelpText("after", `
|
|
9
|
+
Agent notes:
|
|
10
|
+
Product commands use core APIs, not local files. After product create, the product is selected in ~/.farthershore/config.json.
|
|
11
|
+
GitHub, UI, and CLI edits are proposals. product config/status read the accepted internal config.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
farthershore product create weather-api --base-url https://api.example.com --strategy prepaid_credits --format json
|
|
15
|
+
farthershore product status --format json
|
|
16
|
+
farthershore product config --format yaml
|
|
17
|
+
farthershore product attempts --format json
|
|
18
|
+
`);
|
|
6
19
|
product
|
|
7
20
|
.command("create <name>")
|
|
8
|
-
.description("Create a product and select it for
|
|
21
|
+
.description("Create a product, initialize accepted config, and select it for later commands")
|
|
9
22
|
.requiredOption("--base-url <url>", "Backend API base URL")
|
|
10
|
-
.option("--strategy <strategy>", "Product billing strategy", "flat_subscription")
|
|
23
|
+
.option("--strategy <strategy>", "Product billing strategy: free_trial, flat_subscription, included_usage, subscription_overage, pay_as_you_go, prepaid_credits", "flat_subscription")
|
|
11
24
|
.option("--description <desc>", "Product description")
|
|
12
25
|
.option("--display-name <name>", "Display name")
|
|
13
26
|
.option("--dry-run", "Print the create request without mutating")
|
|
27
|
+
.addHelpText("after", `
|
|
28
|
+
Examples:
|
|
29
|
+
farthershore product create weather-api --base-url https://api.example.com --format json
|
|
30
|
+
farthershore product create ai-gateway --base-url https://api.example.com --strategy prepaid_credits --format json
|
|
31
|
+
|
|
32
|
+
Next commands:
|
|
33
|
+
farthershore meter add --help
|
|
34
|
+
farthershore feature add --help
|
|
35
|
+
farthershore plan add --help
|
|
36
|
+
`)
|
|
14
37
|
.action(async (name, opts) => {
|
|
15
38
|
if (opts.dryRun) {
|
|
16
39
|
printResult(program, "Product create dry run", {
|
|
@@ -64,6 +87,10 @@ export function registerProductCommands(program, getClient) {
|
|
|
64
87
|
.command("status")
|
|
65
88
|
.description("Show selected product status")
|
|
66
89
|
.option("--product <product>", "Product id or name")
|
|
90
|
+
.addHelpText("after", `
|
|
91
|
+
Example:
|
|
92
|
+
farthershore product status --format json
|
|
93
|
+
`)
|
|
67
94
|
.action(async (opts) => {
|
|
68
95
|
const client = getClient();
|
|
69
96
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -76,9 +103,14 @@ export function registerProductCommands(program, getClient) {
|
|
|
76
103
|
});
|
|
77
104
|
product
|
|
78
105
|
.command("config")
|
|
79
|
-
.description("Show latest accepted internal product config")
|
|
106
|
+
.description("Show latest accepted internal product config as json or yaml")
|
|
80
107
|
.option("--product <product>", "Product id or name")
|
|
81
108
|
.option("--format <format>", "Output format: json or yaml")
|
|
109
|
+
.addHelpText("after", `
|
|
110
|
+
Examples:
|
|
111
|
+
farthershore product config --format json
|
|
112
|
+
farthershore product config --format yaml
|
|
113
|
+
`)
|
|
82
114
|
.action(async (opts) => {
|
|
83
115
|
const client = getClient();
|
|
84
116
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -93,6 +125,10 @@ export function registerProductCommands(program, getClient) {
|
|
|
93
125
|
.command("attempts")
|
|
94
126
|
.description("Show rejected product config attempts")
|
|
95
127
|
.option("--product <product>", "Product id or name")
|
|
128
|
+
.addHelpText("after", `
|
|
129
|
+
Example:
|
|
130
|
+
farthershore product attempts --format json
|
|
131
|
+
`)
|
|
96
132
|
.action(async (opts) => {
|
|
97
133
|
const client = getClient();
|
|
98
134
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -7,13 +7,30 @@ import { analyzePlanTransition } from "./plan-transition.js";
|
|
|
7
7
|
export function registerTransitionCommands(program, getClient) {
|
|
8
8
|
const transition = program
|
|
9
9
|
.command("transition")
|
|
10
|
-
.description("Preview subscriber impact for product config changes")
|
|
10
|
+
.description("Preview subscriber impact for product config changes")
|
|
11
|
+
.addHelpText("after", `
|
|
12
|
+
Agent notes:
|
|
13
|
+
New subscribers use accepted config immediately. Existing subscribers follow billing.subscriberChangePolicy.
|
|
14
|
+
Preview before price, feature, limit, credit, rating, or strategy changes.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
farthershore transition preview --format json
|
|
18
|
+
farthershore transition preview --to product.yaml --format json
|
|
19
|
+
`);
|
|
11
20
|
transition
|
|
12
21
|
.command("preview")
|
|
13
22
|
.description("Compare the latest accepted config to a proposed config and show subscriber transition impact")
|
|
14
23
|
.option("--product <product>", "Product id or name")
|
|
15
24
|
.option("--to <file>", "Proposed product YAML file")
|
|
16
25
|
.option("--format <format>", "Output format: table or json")
|
|
26
|
+
.addHelpText("after", `
|
|
27
|
+
Examples:
|
|
28
|
+
farthershore transition preview --format json
|
|
29
|
+
farthershore transition preview --to product.yaml --format json
|
|
30
|
+
|
|
31
|
+
Output includes:
|
|
32
|
+
ok, productId, transitionPreview, errors, warnings, nextActions
|
|
33
|
+
`)
|
|
17
34
|
.action(async (opts) => {
|
|
18
35
|
const client = getClient();
|
|
19
36
|
const productId = await resolveProductId(client, opts.product);
|
|
@@ -170,7 +170,17 @@ function reportValidationResult(result) {
|
|
|
170
170
|
export function registerValidateCommand(program) {
|
|
171
171
|
program
|
|
172
172
|
.command("validate [file]")
|
|
173
|
-
.description("Validate a local product.yaml file")
|
|
173
|
+
.description("Validate a local product.yaml file without API calls")
|
|
174
|
+
.addHelpText("after", `
|
|
175
|
+
Agent notes:
|
|
176
|
+
Use validate before committing product.yaml changes. This is local schema validation only.
|
|
177
|
+
Use apply --dry-run for remote compiler validation against pushed branch state.
|
|
178
|
+
|
|
179
|
+
Examples:
|
|
180
|
+
farthershore validate
|
|
181
|
+
farthershore validate product.yaml
|
|
182
|
+
farthershore apply --dry-run --format json
|
|
183
|
+
`)
|
|
174
184
|
// Action body is sync; commander only requires a thenable on
|
|
175
185
|
// .action when await is used. Drop `async` keyword.
|
|
176
186
|
.action((file) => {
|
package/dist/index.js
CHANGED
|
@@ -24,11 +24,38 @@ const pkg = JSON.parse(await readFile(join(__dirname, "..", "package.json"), "ut
|
|
|
24
24
|
const program = new Command();
|
|
25
25
|
program
|
|
26
26
|
.name("farthershore")
|
|
27
|
-
.description("FartherShore CLI — create and
|
|
27
|
+
.description("FartherShore CLI — agent-friendly commands to create, configure, validate, and launch API products")
|
|
28
28
|
.version(pkg.version)
|
|
29
29
|
.option("--token <token>", "Override auth token")
|
|
30
30
|
.option("--api-url <url>", "Override API base URL")
|
|
31
|
-
.option("--format <format>", "Output format
|
|
31
|
+
.option("--format <format>", "Output format. Use --format json for stable machine-readable agent output")
|
|
32
|
+
.addHelpText("after", `
|
|
33
|
+
Agent discovery:
|
|
34
|
+
Start with:
|
|
35
|
+
farthershore --help
|
|
36
|
+
farthershore product --help
|
|
37
|
+
farthershore meter add --help
|
|
38
|
+
farthershore feature add --help
|
|
39
|
+
farthershore plan add --help
|
|
40
|
+
farthershore transition preview --help
|
|
41
|
+
farthershore apply --help
|
|
42
|
+
|
|
43
|
+
Agent end-to-end API workflow:
|
|
44
|
+
farthershore auth set-key "$FARTHERSHORE_TOKEN"
|
|
45
|
+
farthershore product create weather-api --base-url https://api.example.com --strategy prepaid_credits --format json
|
|
46
|
+
farthershore meter add ai_tokens --selector model --measure input_tokens --measure output_tokens --rating provider_catalog --catalog llm_models --format json
|
|
47
|
+
farthershore feature add chat_completions --route POST:/v1/chat/completions --format json
|
|
48
|
+
farthershore plan add free --free --limit ai_tokens:month:100000:enforce --feature chat_completions --format json
|
|
49
|
+
farthershore plan add pro --price-monthly 2900 --credits-monthly-micros 500000000 --meter ai_tokens --feature chat_completions --format json
|
|
50
|
+
farthershore transition preview --format json
|
|
51
|
+
farthershore apply --dry-run --format json
|
|
52
|
+
farthershore product status --format json
|
|
53
|
+
|
|
54
|
+
Useful constants:
|
|
55
|
+
Billing strategies: free_trial, flat_subscription, included_usage, subscription_overage, pay_as_you_go, prepaid_credits
|
|
56
|
+
Rating sources: fixed, provider_catalog, upstream_reported, external_rate_api, custom
|
|
57
|
+
Transition actions: preserve_current_period, switch_immediately, switch_immediately_prorate, new_subscribers_only
|
|
58
|
+
`);
|
|
32
59
|
// Lazy client — created on first use with resolved auth
|
|
33
60
|
function getClient() {
|
|
34
61
|
const config = loadConfig();
|