scaledthought 0.3.0 → 0.3.1
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 +8 -0
- package/dist/index.js +1163 -210
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -553,7 +553,7 @@ import { join } from "node:path";
|
|
|
553
553
|
// package.json
|
|
554
554
|
var package_default = {
|
|
555
555
|
name: "scaledthought",
|
|
556
|
-
version: "0.3.
|
|
556
|
+
version: "0.3.1",
|
|
557
557
|
description: "Headless open-model inference, run by your agent. Prepaid, pay per token. US or Canada.",
|
|
558
558
|
license: "MIT",
|
|
559
559
|
keywords: [
|
|
@@ -661,7 +661,7 @@ var HINTS = {
|
|
|
661
661
|
free_tier_limit: "Accounts that haven't bought credits have lower limits. The first credit purchase lifts them: run `billing topup --amount-usd 20` and show your human the approval_url.",
|
|
662
662
|
free_tier_key_limit: "Accounts that haven't bought credits have at most 5 active keys. Revoke one with `keys revoke <id>`, or buy credits with `billing topup`.",
|
|
663
663
|
too_many_pending_topups: "Two approval links are still unpaid. Ask your human to use one (see `billing status` pending_topups), or wait for them to expire.",
|
|
664
|
-
daily_topup_limit: "Top-ups are limited to $2,000 per account per rolling 24 hours (open links and auto-reloads included). Use a smaller amount (see available_usd) or wait.",
|
|
664
|
+
daily_topup_limit: "Top-ups are limited to $2,000 per account per rolling 24 hours (open links and auto-reloads included). Use a smaller amount (see available_usd) or wait. Need more than $2,000 a day? Invoiced prepayment: sales@scaledthought.com",
|
|
665
665
|
billing_upgrade_removed: "Run `billing topup --amount-usd 20` and show your human the approval_url.",
|
|
666
666
|
gone: "This endpoint was removed. For credits use `billing topup --amount-usd 20`.",
|
|
667
667
|
fine_tuning_requires_self_hosted: "This base model can't be fine-tuned in your region (it isn't served on our own GPUs). Choose another base model.",
|
|
@@ -7898,6 +7898,179 @@ var AD_WRITE_CREDENTIAL_ENV = {
|
|
|
7898
7898
|
microsoft: "AD_WRITE_MICROSOFT_CREDENTIALS"
|
|
7899
7899
|
};
|
|
7900
7900
|
var AD_WRITE_CREDENTIAL_ENV_NAMES = Object.values(AD_WRITE_CREDENTIAL_ENV);
|
|
7901
|
+
// ../../packages/growth/src/portfolio.ts
|
|
7902
|
+
var PORTFOLIO_LIMITS = {
|
|
7903
|
+
weeklyBudgetMicros: 1e9,
|
|
7904
|
+
founders: ["alexey-scaled-throught", "josh-scaledthought"],
|
|
7905
|
+
effortDayMicros: 50000000
|
|
7906
|
+
};
|
|
7907
|
+
var FUNNEL_STAGES = ["click", "fetch", "signup", "activated", "paid"];
|
|
7908
|
+
var EXPERIMENT_KINDS = ["paid", "owned", "onboarding", "lifecycle", "community", "research"];
|
|
7909
|
+
var AGENT_ROLES = [
|
|
7910
|
+
"growth-lead",
|
|
7911
|
+
"analyst",
|
|
7912
|
+
"onboarding",
|
|
7913
|
+
"seo",
|
|
7914
|
+
"paid-ads",
|
|
7915
|
+
"landing",
|
|
7916
|
+
"lifecycle",
|
|
7917
|
+
"content",
|
|
7918
|
+
"community",
|
|
7919
|
+
"research",
|
|
7920
|
+
"integrations",
|
|
7921
|
+
"canada"
|
|
7922
|
+
];
|
|
7923
|
+
var EXPERIMENT_STATUSES = ["proposed", "approved", "running", "read", "killed", "scaled"];
|
|
7924
|
+
var EXPERIMENT_METRICS = [
|
|
7925
|
+
"ctr",
|
|
7926
|
+
"fetch_rate",
|
|
7927
|
+
"agent_fetches",
|
|
7928
|
+
"human_visits",
|
|
7929
|
+
"signups",
|
|
7930
|
+
"activation_rate",
|
|
7931
|
+
"paid_rate",
|
|
7932
|
+
"topup_request_rate",
|
|
7933
|
+
"topup_completion_rate",
|
|
7934
|
+
"cost_per_activated",
|
|
7935
|
+
"cost_per_payer",
|
|
7936
|
+
"onboarding_success_rate",
|
|
7937
|
+
"time_to_first_call",
|
|
7938
|
+
"npm_downloads",
|
|
7939
|
+
"github_stars",
|
|
7940
|
+
"registry_installs"
|
|
7941
|
+
];
|
|
7942
|
+
var SURFACE_TYPES = [
|
|
7943
|
+
"page",
|
|
7944
|
+
"copy",
|
|
7945
|
+
"code",
|
|
7946
|
+
"listing",
|
|
7947
|
+
"cli",
|
|
7948
|
+
"notice",
|
|
7949
|
+
"ad",
|
|
7950
|
+
"post",
|
|
7951
|
+
"email",
|
|
7952
|
+
"external",
|
|
7953
|
+
"price"
|
|
7954
|
+
];
|
|
7955
|
+
var TIER2_SURFACES = new Set(["ad", "post", "email", "price", "listing", "external"]);
|
|
7956
|
+
var SURFACE_RE = new RegExp(`^(${SURFACE_TYPES.join("|")}):[^\\s*]+\\*?$`);
|
|
7957
|
+
var EXPERIMENT_ID_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
7958
|
+
var CAMPAIGN_ID_RE = /^gcm_[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
7959
|
+
var Day = string2().refine(isDay, "a UTC day, YYYY-MM-DD");
|
|
7960
|
+
var Bps = number2().int().min(0).max(1e4);
|
|
7961
|
+
var Micros = number2().int().min(0);
|
|
7962
|
+
var DecisionRule = discriminatedUnion("kind", [
|
|
7963
|
+
object({ kind: literal("p_better"), threshold: number2().min(0.5).max(0.999) }),
|
|
7964
|
+
object({ kind: literal("rate_at_least"), bps: Bps }),
|
|
7965
|
+
object({ kind: literal("cost_at_most"), micros: Micros })
|
|
7966
|
+
]);
|
|
7967
|
+
var ExperimentSpec = object({
|
|
7968
|
+
id: string2().max(64).regex(EXPERIMENT_ID_RE, "kebab-case, the file name without .yaml"),
|
|
7969
|
+
title: string2().min(5).max(120),
|
|
7970
|
+
hypothesis: string2().min(20).max(600),
|
|
7971
|
+
owner: _enum(AGENT_ROLES),
|
|
7972
|
+
kind: _enum(EXPERIMENT_KINDS),
|
|
7973
|
+
stage: _enum(FUNNEL_STAGES),
|
|
7974
|
+
tier: union([literal(0), literal(1), literal(2)]),
|
|
7975
|
+
status: _enum(EXPERIMENT_STATUSES),
|
|
7976
|
+
created: Day,
|
|
7977
|
+
surfaces: array(string2().regex(SURFACE_RE, "<type>:<target>, e.g. page:/docs/cursor")).min(1),
|
|
7978
|
+
codes: array(CodeSchema).default([]),
|
|
7979
|
+
metric: object({ primary: _enum(EXPERIMENT_METRICS), guardrail: _enum(EXPERIMENT_METRICS).optional() }),
|
|
7980
|
+
design: object({
|
|
7981
|
+
type: _enum(["ab", "single", "discovery", "synthetic"]),
|
|
7982
|
+
variants: array(string2().min(1).max(40)).optional(),
|
|
7983
|
+
arms: record(string2().min(1).max(40), array(CodeSchema).min(1)).optional()
|
|
7984
|
+
}),
|
|
7985
|
+
run: object({ from: Day, to: Day }).optional(),
|
|
7986
|
+
decision: object({ read_on: Day, min_n: number2().int().min(1), rule: DecisionRule }),
|
|
7987
|
+
budget: object({ weekly_micros: Micros }).default({ weekly_micros: 0 }),
|
|
7988
|
+
estimate: object({
|
|
7989
|
+
p_win_bps: Bps,
|
|
7990
|
+
lift_bps: number2().int().min(1).max(1e5),
|
|
7991
|
+
baseline_per_week: number2().min(0),
|
|
7992
|
+
effort_days: number2().min(0).max(30)
|
|
7993
|
+
}),
|
|
7994
|
+
approval: object({ by: string2().optional(), on: Day.optional(), note: string2().min(3).max(300).optional() }).strict().optional(),
|
|
7995
|
+
campaigns: array(string2().regex(CAMPAIGN_ID_RE, "a console campaign id, gcm_<ulid>")).max(10).default([]),
|
|
7996
|
+
result: object({
|
|
7997
|
+
read: Day,
|
|
7998
|
+
outcome: _enum(["win", "loss", "inconclusive", "stopped"]),
|
|
7999
|
+
summary: string2().min(10).max(1000)
|
|
8000
|
+
}).optional()
|
|
8001
|
+
}).strict();
|
|
8002
|
+
|
|
8003
|
+
// ../../packages/growth/src/campaigns.ts
|
|
8004
|
+
var KEYWORD_MATCH_TYPES = ["exact", "phrase", "broad"];
|
|
8005
|
+
var GOOGLE_AD_LIMITS = {
|
|
8006
|
+
headline: 30,
|
|
8007
|
+
description: 90,
|
|
8008
|
+
path: 15,
|
|
8009
|
+
headlinesMin: 3,
|
|
8010
|
+
headlinesMax: 15,
|
|
8011
|
+
descriptionsMin: 2,
|
|
8012
|
+
descriptionsMax: 4,
|
|
8013
|
+
keyword: 80,
|
|
8014
|
+
keywordWords: 10,
|
|
8015
|
+
adGroups: 20,
|
|
8016
|
+
keywordsPerAdGroup: 200,
|
|
8017
|
+
adsPerAdGroup: 3,
|
|
8018
|
+
negativeKeywords: 500
|
|
8019
|
+
};
|
|
8020
|
+
var CampaignId = string2().regex(CAMPAIGN_ID_RE, "a campaign id, gcm_<ulid>");
|
|
8021
|
+
var Line = (max, what) => string2().trim().min(1).refine((s) => [...s].length <= max, `${what}: at most ${max} characters`);
|
|
8022
|
+
var Keyword = object({
|
|
8023
|
+
text: Line(GOOGLE_AD_LIMITS.keyword, "keyword").refine((s) => s.split(/\s+/).length <= GOOGLE_AD_LIMITS.keywordWords, `keyword: at most ${GOOGLE_AD_LIMITS.keywordWords} words`),
|
|
8024
|
+
match: _enum(KEYWORD_MATCH_TYPES)
|
|
8025
|
+
}).strict();
|
|
8026
|
+
var SearchAd = object({
|
|
8027
|
+
headlines: array(Line(GOOGLE_AD_LIMITS.headline, "headline")).min(GOOGLE_AD_LIMITS.headlinesMin).max(GOOGLE_AD_LIMITS.headlinesMax),
|
|
8028
|
+
descriptions: array(Line(GOOGLE_AD_LIMITS.description, "description")).min(GOOGLE_AD_LIMITS.descriptionsMin).max(GOOGLE_AD_LIMITS.descriptionsMax),
|
|
8029
|
+
path1: Line(GOOGLE_AD_LIMITS.path, "path").optional(),
|
|
8030
|
+
path2: Line(GOOGLE_AD_LIMITS.path, "path").optional()
|
|
8031
|
+
}).strict().refine((a) => !a.path2 || a.path1, "path2 needs path1");
|
|
8032
|
+
var AdGroup2 = object({
|
|
8033
|
+
name: string2().trim().min(1).max(255),
|
|
8034
|
+
keywords: array(Keyword).min(1).max(GOOGLE_AD_LIMITS.keywordsPerAdGroup),
|
|
8035
|
+
ads: array(SearchAd).min(1).max(GOOGLE_AD_LIMITS.adsPerAdGroup),
|
|
8036
|
+
negative_keywords: array(Keyword).max(GOOGLE_AD_LIMITS.negativeKeywords).default([])
|
|
8037
|
+
}).strict();
|
|
8038
|
+
var CampaignContent = object({
|
|
8039
|
+
ad_groups: array(AdGroup2).min(1).max(GOOGLE_AD_LIMITS.adGroups),
|
|
8040
|
+
negative_keywords: array(Keyword).max(GOOGLE_AD_LIMITS.negativeKeywords).default([])
|
|
8041
|
+
}).strict();
|
|
8042
|
+
var CAMPAIGN_NETWORKS = ["search", "search_partners", "display"];
|
|
8043
|
+
var CampaignTargeting = object({
|
|
8044
|
+
locations: array(string2().trim().min(2).max(80)).min(1).max(50),
|
|
8045
|
+
location_mode: _enum(["presence", "presence_or_interest"]).default("presence"),
|
|
8046
|
+
languages: array(string2().trim().min(2).max(10)).min(1).max(20),
|
|
8047
|
+
networks: array(_enum(CAMPAIGN_NETWORKS)).min(1).max(3).default(["search"])
|
|
8048
|
+
}).strict();
|
|
8049
|
+
var Day2 = string2().refine(isDay, "a UTC day, YYYY-MM-DD");
|
|
8050
|
+
var Budget = number2().int().positive().max(PORTFOLIO_LIMITS.weeklyBudgetMicros);
|
|
8051
|
+
var CAMPAIGN_FIELD_SHAPE = {
|
|
8052
|
+
name: string2().trim().min(3).max(120),
|
|
8053
|
+
code: CodeSchema,
|
|
8054
|
+
experiment_id: string2().max(64).regex(EXPERIMENT_ID_RE).nullable(),
|
|
8055
|
+
ad_account_id: string2().trim().min(1).max(64).nullable(),
|
|
8056
|
+
final_url: string2().trim().max(200),
|
|
8057
|
+
daily_budget_micros: Budget.nullable(),
|
|
8058
|
+
weekly_budget_micros: Budget.nullable(),
|
|
8059
|
+
run_from: Day2,
|
|
8060
|
+
run_to: Day2,
|
|
8061
|
+
targeting: CampaignTargeting,
|
|
8062
|
+
content: CampaignContent,
|
|
8063
|
+
notes: string2().trim().max(4000).nullable()
|
|
8064
|
+
};
|
|
8065
|
+
var CampaignFields = object({
|
|
8066
|
+
...CAMPAIGN_FIELD_SHAPE,
|
|
8067
|
+
experiment_id: CAMPAIGN_FIELD_SHAPE.experiment_id.default(null),
|
|
8068
|
+
ad_account_id: CAMPAIGN_FIELD_SHAPE.ad_account_id.default(null),
|
|
8069
|
+
daily_budget_micros: CAMPAIGN_FIELD_SHAPE.daily_budget_micros.default(null),
|
|
8070
|
+
weekly_budget_micros: CAMPAIGN_FIELD_SHAPE.weekly_budget_micros.default(null),
|
|
8071
|
+
notes: CAMPAIGN_FIELD_SHAPE.notes.default(null)
|
|
8072
|
+
}).strict();
|
|
8073
|
+
var CampaignFieldsPatch = object(CAMPAIGN_FIELD_SHAPE).partial().strict();
|
|
7901
8074
|
// ../../packages/growth/src/sinks/google.ts
|
|
7902
8075
|
var GOOGLE_DATA_MANAGER_URL = "https://datamanager.googleapis.com/v1/events:ingest";
|
|
7903
8076
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -8208,35 +8381,9 @@ var PERMANENT_FIRST_PURCHASE_SKIPS = new Set([
|
|
|
8208
8381
|
"account_not_active"
|
|
8209
8382
|
]);
|
|
8210
8383
|
// ../../packages/growth/src/experiments.ts
|
|
8211
|
-
var EXPERIMENTS = {
|
|
8212
|
-
"first-paying-customers": {
|
|
8213
|
-
title: "First paying customers",
|
|
8214
|
-
hypothesis: "Developers who use coding agents will pay for open models when their agent sets everything up from one pasted prompt.",
|
|
8215
|
-
spend: { from: "2026-10-06", to: "2026-10-19" },
|
|
8216
|
-
tailDays: 7,
|
|
8217
|
-
payWindowDays: 14,
|
|
8218
|
-
activationDays: 7,
|
|
8219
|
-
passCostMicros: 20000000,
|
|
8220
|
-
stopCostMicros: 40000000,
|
|
8221
|
-
minPaidRateBps: 500,
|
|
8222
|
-
lowPaidRateBps: 200,
|
|
8223
|
-
minPayers: 12,
|
|
8224
|
-
verdictMinSpendMicros: 300000000,
|
|
8225
|
-
verdictMinActivations: 15,
|
|
8226
|
-
pauseCheckDay: 10,
|
|
8227
|
-
readMinMatured: 30,
|
|
8228
|
-
excludedAccountIds: [],
|
|
8229
|
-
messages: { A: "Agent", B: "Free", C: "Private" },
|
|
8230
|
-
platforms: ["reddit", "google", "x", "ethicalads"],
|
|
8231
|
-
plan: {
|
|
8232
|
-
fetchPerClickBps: 2000,
|
|
8233
|
-
activatedPerFetchBps: 7500,
|
|
8234
|
-
paidPerActivatedBps: 500,
|
|
8235
|
-
costPerPayerMicros: 333000000
|
|
8236
|
-
}
|
|
8237
|
-
}
|
|
8238
|
-
};
|
|
8384
|
+
var EXPERIMENTS = {};
|
|
8239
8385
|
var EXPERIMENT_KEYS = Object.keys(EXPERIMENTS);
|
|
8386
|
+
var DEFAULT_EXPERIMENT_KEY = EXPERIMENT_KEYS[0] ?? null;
|
|
8240
8387
|
var EXPERIMENT_PHASES = ["before", "spending", "reading_payment", "payment_read", "tail_read"];
|
|
8241
8388
|
// ../../packages/growth/src/go-hits.ts
|
|
8242
8389
|
var GO_HIT_KINDS = ["human", "agent"];
|
|
@@ -8297,13 +8444,13 @@ var EthicalAdsCredentials = object({
|
|
|
8297
8444
|
var credentials3 = (creds) => parseCredentialsJson(creds[ENV7], EthicalAdsCredentials);
|
|
8298
8445
|
var ethicalAdsApiAccount = (creds) => credentials3(creds).advertiser;
|
|
8299
8446
|
var Num3 = union([number2(), string2()]).nullable().optional();
|
|
8300
|
-
var
|
|
8447
|
+
var Day3 = object({ date: string2().optional(), index: string2().optional(), views: Num3, clicks: Num3, cost: Num3 });
|
|
8301
8448
|
var Report = object({
|
|
8302
|
-
days: array(
|
|
8449
|
+
days: array(Day3).default([]),
|
|
8303
8450
|
flights: array(object({
|
|
8304
8451
|
name: string2().nullable().optional(),
|
|
8305
8452
|
slug: string2(),
|
|
8306
|
-
report: object({ days: array(
|
|
8453
|
+
report: object({ days: array(Day3).default([]) }).nullable().optional()
|
|
8307
8454
|
})).default([])
|
|
8308
8455
|
});
|
|
8309
8456
|
var dayOf2 = (d) => (d.date ?? d.index ?? "").slice(0, 10);
|
|
@@ -8949,102 +9096,8 @@ var ethicalAds2 = withApi(ethicalAds, "ethicalads", pullEthicalAds, ethicalAdsAp
|
|
|
8949
9096
|
var microsoftAds2 = withApi(microsoftAds, "microsoft", pullMicrosoftAds, microsoftApiAccount);
|
|
8950
9097
|
var linkedInAds2 = withApi(linkedInAds, "linkedin", pullLinkedInAds, linkedInApiAccount);
|
|
8951
9098
|
var metaAds2 = withApi(metaAds, "meta", pullMetaAds, metaApiAccount);
|
|
8952
|
-
// ../../packages/growth/src/portfolio.ts
|
|
8953
|
-
var FUNNEL_STAGES = ["click", "fetch", "signup", "activated", "paid"];
|
|
8954
|
-
var EXPERIMENT_KINDS = ["paid", "owned", "onboarding", "lifecycle", "community", "research"];
|
|
8955
|
-
var AGENT_ROLES = [
|
|
8956
|
-
"growth-lead",
|
|
8957
|
-
"analyst",
|
|
8958
|
-
"onboarding",
|
|
8959
|
-
"seo",
|
|
8960
|
-
"paid-ads",
|
|
8961
|
-
"landing",
|
|
8962
|
-
"lifecycle",
|
|
8963
|
-
"content",
|
|
8964
|
-
"community",
|
|
8965
|
-
"research",
|
|
8966
|
-
"integrations",
|
|
8967
|
-
"canada"
|
|
8968
|
-
];
|
|
8969
|
-
var EXPERIMENT_STATUSES = ["proposed", "approved", "running", "read", "killed", "scaled"];
|
|
8970
|
-
var EXPERIMENT_METRICS = [
|
|
8971
|
-
"ctr",
|
|
8972
|
-
"fetch_rate",
|
|
8973
|
-
"agent_fetches",
|
|
8974
|
-
"human_visits",
|
|
8975
|
-
"signups",
|
|
8976
|
-
"activation_rate",
|
|
8977
|
-
"paid_rate",
|
|
8978
|
-
"topup_request_rate",
|
|
8979
|
-
"topup_completion_rate",
|
|
8980
|
-
"cost_per_activated",
|
|
8981
|
-
"cost_per_payer",
|
|
8982
|
-
"onboarding_success_rate",
|
|
8983
|
-
"time_to_first_call",
|
|
8984
|
-
"npm_downloads",
|
|
8985
|
-
"github_stars",
|
|
8986
|
-
"registry_installs"
|
|
8987
|
-
];
|
|
8988
|
-
var SURFACE_TYPES = [
|
|
8989
|
-
"page",
|
|
8990
|
-
"copy",
|
|
8991
|
-
"code",
|
|
8992
|
-
"listing",
|
|
8993
|
-
"cli",
|
|
8994
|
-
"notice",
|
|
8995
|
-
"ad",
|
|
8996
|
-
"post",
|
|
8997
|
-
"email",
|
|
8998
|
-
"external",
|
|
8999
|
-
"price"
|
|
9000
|
-
];
|
|
9001
|
-
var TIER2_SURFACES = new Set(["ad", "post", "email", "price", "listing", "external"]);
|
|
9002
|
-
var SURFACE_RE = new RegExp(`^(${SURFACE_TYPES.join("|")}):[^\\s*]+\\*?$`);
|
|
9003
|
-
var EXPERIMENT_ID_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
9004
|
-
var Day2 = string2().refine(isDay, "a UTC day, YYYY-MM-DD");
|
|
9005
|
-
var Bps = number2().int().min(0).max(1e4);
|
|
9006
|
-
var Micros = number2().int().min(0);
|
|
9007
|
-
var DecisionRule = discriminatedUnion("kind", [
|
|
9008
|
-
object({ kind: literal("p_better"), threshold: number2().min(0.5).max(0.999) }),
|
|
9009
|
-
object({ kind: literal("rate_at_least"), bps: Bps }),
|
|
9010
|
-
object({ kind: literal("cost_at_most"), micros: Micros })
|
|
9011
|
-
]);
|
|
9012
|
-
var ExperimentSpec = object({
|
|
9013
|
-
id: string2().max(64).regex(EXPERIMENT_ID_RE, "kebab-case, the file name without .yaml"),
|
|
9014
|
-
title: string2().min(5).max(120),
|
|
9015
|
-
hypothesis: string2().min(20).max(600),
|
|
9016
|
-
owner: _enum(AGENT_ROLES),
|
|
9017
|
-
kind: _enum(EXPERIMENT_KINDS),
|
|
9018
|
-
stage: _enum(FUNNEL_STAGES),
|
|
9019
|
-
tier: union([literal(0), literal(1), literal(2)]),
|
|
9020
|
-
status: _enum(EXPERIMENT_STATUSES),
|
|
9021
|
-
created: Day2,
|
|
9022
|
-
surfaces: array(string2().regex(SURFACE_RE, "<type>:<target>, e.g. page:/docs/cursor")).min(1),
|
|
9023
|
-
codes: array(CodeSchema).default([]),
|
|
9024
|
-
metric: object({ primary: _enum(EXPERIMENT_METRICS), guardrail: _enum(EXPERIMENT_METRICS).optional() }),
|
|
9025
|
-
design: object({
|
|
9026
|
-
type: _enum(["ab", "single", "discovery", "synthetic"]),
|
|
9027
|
-
variants: array(string2().min(1).max(40)).optional(),
|
|
9028
|
-
arms: record(string2().min(1).max(40), array(CodeSchema).min(1)).optional()
|
|
9029
|
-
}),
|
|
9030
|
-
run: object({ from: Day2, to: Day2 }).optional(),
|
|
9031
|
-
decision: object({ read_on: Day2, min_n: number2().int().min(1), rule: DecisionRule }),
|
|
9032
|
-
budget: object({ weekly_micros: Micros }).default({ weekly_micros: 0 }),
|
|
9033
|
-
estimate: object({
|
|
9034
|
-
p_win_bps: Bps,
|
|
9035
|
-
lift_bps: number2().int().min(1).max(1e5),
|
|
9036
|
-
baseline_per_week: number2().min(0),
|
|
9037
|
-
effort_days: number2().min(0).max(30)
|
|
9038
|
-
}),
|
|
9039
|
-
approval: object({ by: string2(), on: Day2 }).optional(),
|
|
9040
|
-
result: object({
|
|
9041
|
-
read: Day2,
|
|
9042
|
-
outcome: _enum(["win", "loss", "inconclusive", "stopped"]),
|
|
9043
|
-
summary: string2().min(10).max(1000)
|
|
9044
|
-
}).optional()
|
|
9045
|
-
}).strict();
|
|
9046
9099
|
// ../../packages/growth/src/results-schema.ts
|
|
9047
|
-
var
|
|
9100
|
+
var Day4 = string2().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
9048
9101
|
var Micros2 = number2().int();
|
|
9049
9102
|
var Count = number2().int().nonnegative();
|
|
9050
9103
|
var Bps2 = number2().int();
|
|
@@ -9076,7 +9129,7 @@ var CodeResultRow = ResultRow.extend({
|
|
|
9076
9129
|
channel: string2(),
|
|
9077
9130
|
message: string2().nullable(),
|
|
9078
9131
|
status: string2(),
|
|
9079
|
-
paused_on:
|
|
9132
|
+
paused_on: Day4.nullable(),
|
|
9080
9133
|
sample_ok: boolean2(),
|
|
9081
9134
|
pause_suggested: boolean2(),
|
|
9082
9135
|
leading: boolean2(),
|
|
@@ -9108,8 +9161,8 @@ var Scorecard = object({
|
|
|
9108
9161
|
real_use: object({ eligible: Count, returned: Count, rate_bps: Bps2.nullable() })
|
|
9109
9162
|
});
|
|
9110
9163
|
var Read = object({
|
|
9111
|
-
signups_from:
|
|
9112
|
-
signups_to:
|
|
9164
|
+
signups_from: Day4,
|
|
9165
|
+
signups_to: Day4,
|
|
9113
9166
|
scorecard: Scorecard,
|
|
9114
9167
|
by_message: array(ResultRow),
|
|
9115
9168
|
by_channel: array(ResultRow),
|
|
@@ -9131,7 +9184,7 @@ var RuleOutcome = _enum([
|
|
|
9131
9184
|
]);
|
|
9132
9185
|
var Rule = object({
|
|
9133
9186
|
id: RuleId,
|
|
9134
|
-
date:
|
|
9187
|
+
date: Day4,
|
|
9135
9188
|
status: RuleStatus,
|
|
9136
9189
|
outcome: RuleOutcome,
|
|
9137
9190
|
codes: array(string2()),
|
|
@@ -9140,7 +9193,7 @@ var Rule = object({
|
|
|
9140
9193
|
var PlatformFinality = object({
|
|
9141
9194
|
platform: string2(),
|
|
9142
9195
|
spend_micros: Micros2,
|
|
9143
|
-
final_through:
|
|
9196
|
+
final_through: Day4.nullable(),
|
|
9144
9197
|
last_import_at: string2().nullable(),
|
|
9145
9198
|
last_import_source: _enum(["csv", "api", "manual"]).nullable()
|
|
9146
9199
|
});
|
|
@@ -9165,13 +9218,13 @@ var Checks = object({
|
|
|
9165
9218
|
}),
|
|
9166
9219
|
spread: array(object({
|
|
9167
9220
|
code: string2(),
|
|
9168
|
-
paused_on:
|
|
9221
|
+
paused_on: Day4,
|
|
9169
9222
|
signups_after: Count,
|
|
9170
9223
|
verdict_changes: boolean2()
|
|
9171
9224
|
})),
|
|
9172
9225
|
spend: object({
|
|
9173
9226
|
final: boolean2(),
|
|
9174
|
-
final_through:
|
|
9227
|
+
final_through: Day4.nullable(),
|
|
9175
9228
|
platforms: array(PlatformFinality),
|
|
9176
9229
|
unattributed_micros: Micros2,
|
|
9177
9230
|
unattributed_rows: Count
|
|
@@ -9197,13 +9250,13 @@ var GrowthResults = object({
|
|
|
9197
9250
|
key: string2(),
|
|
9198
9251
|
title: string2(),
|
|
9199
9252
|
hypothesis: string2(),
|
|
9200
|
-
spend: object({ from:
|
|
9253
|
+
spend: object({ from: Day4, to: Day4 }),
|
|
9201
9254
|
dates: object({
|
|
9202
|
-
pause_check:
|
|
9203
|
-
cost_read:
|
|
9204
|
-
payment_read:
|
|
9205
|
-
tail_read:
|
|
9206
|
-
tail_signups_to:
|
|
9255
|
+
pause_check: Day4,
|
|
9256
|
+
cost_read: Day4,
|
|
9257
|
+
payment_read: Day4,
|
|
9258
|
+
tail_read: Day4,
|
|
9259
|
+
tail_signups_to: Day4
|
|
9207
9260
|
}),
|
|
9208
9261
|
thresholds: object({
|
|
9209
9262
|
pass_cost_micros: Micros2,
|
|
@@ -9224,7 +9277,7 @@ var GrowthResults = object({
|
|
|
9224
9277
|
cost_per_payer_micros: Micros2
|
|
9225
9278
|
})
|
|
9226
9279
|
}),
|
|
9227
|
-
as_of:
|
|
9280
|
+
as_of: Day4,
|
|
9228
9281
|
day: number2().int(),
|
|
9229
9282
|
phase: _enum(EXPERIMENT_PHASES),
|
|
9230
9283
|
read: Read,
|
|
@@ -9236,7 +9289,7 @@ var GrowthResults = object({
|
|
|
9236
9289
|
decisions: array(GrowthDecision)
|
|
9237
9290
|
});
|
|
9238
9291
|
// ../../packages/schemas/src/legal.ts
|
|
9239
|
-
var TERMS_VERSION = "2026-10-
|
|
9292
|
+
var TERMS_VERSION = "2026-10-11";
|
|
9240
9293
|
var LEGAL_SITE = "https://scaledthought.com";
|
|
9241
9294
|
var LEGAL_PATHS = {
|
|
9242
9295
|
index: "/legal",
|
|
@@ -9244,7 +9297,6 @@ var LEGAL_PATHS = {
|
|
|
9244
9297
|
privacy: "/legal/privacy",
|
|
9245
9298
|
acceptableUse: "/legal/acceptable-use",
|
|
9246
9299
|
creditsAndRefunds: "/legal/credits-and-refunds",
|
|
9247
|
-
subprocessors: "/legal/subprocessors",
|
|
9248
9300
|
security: "/legal/security"
|
|
9249
9301
|
};
|
|
9250
9302
|
var TERMS_URL = `${LEGAL_SITE}${LEGAL_PATHS.terms}`;
|
|
@@ -9392,7 +9444,6 @@ function machineStore(env, platform = process.platform) {
|
|
|
9392
9444
|
}
|
|
9393
9445
|
|
|
9394
9446
|
// src/connect.ts
|
|
9395
|
-
var NPX = "npx -y scaledthought@0.3";
|
|
9396
9447
|
var LOGIN_DIR = ".scaledthought";
|
|
9397
9448
|
var LOGIN_FILE = join4(LOGIN_DIR, "login.json");
|
|
9398
9449
|
var KEY_SAVED_TO = ".env (ST_API_KEY, ST_BASE_URL)";
|
|
@@ -9742,10 +9793,10 @@ async function readCodeFrom(stream) {
|
|
|
9742
9793
|
}
|
|
9743
9794
|
|
|
9744
9795
|
// src/commands.ts
|
|
9745
|
-
var
|
|
9796
|
+
var NPX = "npx -y scaledthought@0.3";
|
|
9746
9797
|
var FIRST_TOPUP_USD = 20;
|
|
9747
|
-
var TOPUP_COMMAND = `${
|
|
9748
|
-
var WAIT_COMMAND = `${
|
|
9798
|
+
var TOPUP_COMMAND = `${NPX} billing topup --amount-usd ${FIRST_TOPUP_USD}`;
|
|
9799
|
+
var WAIT_COMMAND = `${NPX} billing wait`;
|
|
9749
9800
|
var WAIT_PATIENCE_MIN = 15;
|
|
9750
9801
|
var CREDIT_STEPS = [
|
|
9751
9802
|
`Inference is prepaid and this account has no credits yet: completions return 402 insufficient_credits until your human pays. Do these first, in order:`,
|
|
@@ -9757,10 +9808,10 @@ var CREDIT_STEPS = [
|
|
|
9757
9808
|
function nextSteps(model, needsCredits) {
|
|
9758
9809
|
return [
|
|
9759
9810
|
...needsCredits ? CREDIT_STEPS : [],
|
|
9760
|
-
model ? `Verify with one real completion: ${
|
|
9761
|
-
`Or let the router pick: ${
|
|
9811
|
+
model ? `Verify with one real completion: ${NPX} chat -m ${model} "Reply with OK" (or POST $ST_BASE_URL/chat/completions with that model)` : `No chat model is live in this region right now. Check again with: ${NPX} models --live`,
|
|
9812
|
+
`Or let the router pick: ${NPX} chat -m auto "Reply with OK"`,
|
|
9762
9813
|
"Use it from code: any OpenAI SDK with base_url=$ST_BASE_URL and api_key=$ST_API_KEY (both are in .env)",
|
|
9763
|
-
`Optional, connect MCP: ${
|
|
9814
|
+
`Optional, connect MCP: ${NPX} mcp`,
|
|
9764
9815
|
...needsCredits ? [] : [`More credits when needed: ${TOPUP_COMMAND} returns an approval_url for your human (inference is prepaid)`]
|
|
9765
9816
|
];
|
|
9766
9817
|
}
|
|
@@ -9780,7 +9831,7 @@ async function liveSummary(ctx, region, balanceUsd) {
|
|
|
9780
9831
|
return {
|
|
9781
9832
|
balance_usd: balanceUsd ?? 0,
|
|
9782
9833
|
needs_credits: needsCredits,
|
|
9783
|
-
next_command: needsCredits ? TOPUP_COMMAND : `${
|
|
9834
|
+
next_command: needsCredits ? TOPUP_COMMAND : `${NPX} chat -m ${suggested ?? "auto"} "Reply with OK"`,
|
|
9784
9835
|
newest_models: live.slice(0, 5),
|
|
9785
9836
|
suggested_model: suggested,
|
|
9786
9837
|
next_steps: nextSteps(suggested, needsCredits)
|
|
@@ -10222,7 +10273,7 @@ async function billingTopupAndWait(ctx, o) {
|
|
|
10222
10273
|
let t = created;
|
|
10223
10274
|
while (!TOPUP_DONE.has(t.status ?? "")) {
|
|
10224
10275
|
if (Date.now() > deadline)
|
|
10225
|
-
throw new CliError("topup_wait_timeout", `Top-up ${id} is still ${t.status}.`, `The link may still work. Check later with: ${
|
|
10276
|
+
throw new CliError("topup_wait_timeout", `Top-up ${id} is still ${t.status}.`, `The link may still work. Check later with: ${NPX} billing status`, { topup_id: id });
|
|
10226
10277
|
await new Promise((r) => setTimeout(r, o.pollMs ?? 3000));
|
|
10227
10278
|
t = await api(ctx, "GET", `/v1/billing/topups/${encodeURIComponent(id)}`);
|
|
10228
10279
|
}
|
|
@@ -10233,7 +10284,7 @@ async function billingTopupAndWait(ctx, o) {
|
|
|
10233
10284
|
var billingAutoReloadOff = (ctx) => api(ctx, "PATCH", "/v1/billing/auto-reload", { body: { enabled: false } });
|
|
10234
10285
|
var billingTransactions = (ctx, o = {}) => api(ctx, "GET", `/v1/billing/transactions${o.cursor ? `?cursor=${encodeURIComponent(o.cursor)}` : ""}`);
|
|
10235
10286
|
function billingUpgradeRemoved() {
|
|
10236
|
-
throw new CliError("billing_upgrade_removed", "billing upgrade (monthly caps) was replaced by prepaid credits.", `Run \`${
|
|
10287
|
+
throw new CliError("billing_upgrade_removed", "billing upgrade (monthly caps) was replaced by prepaid credits.", `Run \`${NPX} billing topup --amount-usd 20\` and send your human the approval_url. They can turn on auto-reload on the same page.`);
|
|
10237
10288
|
}
|
|
10238
10289
|
var benchmarks = (ctx, id, region) => api(ctx, "GET", `/v1/models/${encodeURIComponent(id)}/benchmarks`, {
|
|
10239
10290
|
auth: false,
|
|
@@ -10581,8 +10632,827 @@ async function decide2(ctx, body) {
|
|
|
10581
10632
|
return { ...out, region: res.headers.get("x-st-region"), request_id: res.headers.get("x-st-request-id") };
|
|
10582
10633
|
}
|
|
10583
10634
|
|
|
10635
|
+
// src/destinations/run.ts
|
|
10636
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
10637
|
+
var TIMEOUT_MS2 = 120000;
|
|
10638
|
+
var spawnRunner = (cmd, args, opts) => new Promise((resolve, reject) => {
|
|
10639
|
+
const child = spawn2(cmd, args, {
|
|
10640
|
+
cwd: opts.cwd,
|
|
10641
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
10642
|
+
shell: false,
|
|
10643
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10644
|
+
windowsHide: true
|
|
10645
|
+
});
|
|
10646
|
+
let stdout = "";
|
|
10647
|
+
let stderr = "";
|
|
10648
|
+
const timer = setTimeout(() => child.kill("SIGTERM"), TIMEOUT_MS2);
|
|
10649
|
+
child.stdout.on("data", (d) => {
|
|
10650
|
+
stdout += d.toString("utf8");
|
|
10651
|
+
});
|
|
10652
|
+
child.stderr.on("data", (d) => {
|
|
10653
|
+
stderr += d.toString("utf8");
|
|
10654
|
+
});
|
|
10655
|
+
child.on("error", (e) => {
|
|
10656
|
+
clearTimeout(timer);
|
|
10657
|
+
reject(e);
|
|
10658
|
+
});
|
|
10659
|
+
child.on("close", (code) => {
|
|
10660
|
+
clearTimeout(timer);
|
|
10661
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
10662
|
+
});
|
|
10663
|
+
child.stdin.on("error", () => {});
|
|
10664
|
+
child.stdin.end(opts.stdin ?? "");
|
|
10665
|
+
});
|
|
10666
|
+
var isMissing = (e) => e?.code === "ENOENT";
|
|
10667
|
+
function redact(text, secrets) {
|
|
10668
|
+
let out = text;
|
|
10669
|
+
for (const s of secrets)
|
|
10670
|
+
if (s)
|
|
10671
|
+
out = out.split(s).join("[redacted]");
|
|
10672
|
+
return out.replace(/st_(ca|us)_(live|test)_[0-9A-Za-z]{8,}/g, "[redacted]");
|
|
10673
|
+
}
|
|
10674
|
+
async function probe(io, p, args, stdin) {
|
|
10675
|
+
try {
|
|
10676
|
+
return await io.run(p.cmd, args, { cwd: io.cwd, stdin });
|
|
10677
|
+
} catch (e) {
|
|
10678
|
+
if (isMissing(e))
|
|
10679
|
+
throw missing(p);
|
|
10680
|
+
throw new CliError("destination_failed", `Could not start ${p.cmd}.`, p.install);
|
|
10681
|
+
}
|
|
10682
|
+
}
|
|
10683
|
+
async function exec(io, p, args, o = {}) {
|
|
10684
|
+
const r = await probe(io, p, args, o.stdin);
|
|
10685
|
+
if (r.code !== 0) {
|
|
10686
|
+
const detail = redact(r.stderr || r.stdout, [...o.secrets ?? [], o.stdin ?? ""]).trim().slice(-300);
|
|
10687
|
+
throw new CliError("destination_failed", `\`${o.shown ?? `${p.cmd} ${args.slice(0, 3).join(" ")}`}\` failed (exit code ${r.code}).`, p.login, detail ? { detail } : {});
|
|
10688
|
+
}
|
|
10689
|
+
return r.stdout;
|
|
10690
|
+
}
|
|
10691
|
+
function missing(p) {
|
|
10692
|
+
return new CliError("destination_cli_missing", `\`${p.cmd}\` isn't installed or isn't on PATH.`, p.install);
|
|
10693
|
+
}
|
|
10694
|
+
var SAFE = /^[A-Za-z0-9_.@:+=][A-Za-z0-9_.@:+=-]*$/;
|
|
10695
|
+
var SAFE_SLASH = /^[A-Za-z0-9_.@:+=][A-Za-z0-9_.@:+=/-]*$/;
|
|
10696
|
+
var isSafeValue = (v, o = {}) => typeof v === "string" && v.length <= 120 && (o.slash ? SAFE_SLASH : SAFE).test(v);
|
|
10697
|
+
function safeValue(flag, v, o = {}) {
|
|
10698
|
+
if (!isSafeValue(v, o))
|
|
10699
|
+
throw new CliError("bad_argument", `--${flag} "${v.slice(0, 60)}" isn't a valid name${o.slash ? "" : " (no slashes)"}.`, "Use letters, digits and . _ - : @ (it must not start with -).");
|
|
10700
|
+
return v;
|
|
10701
|
+
}
|
|
10702
|
+
|
|
10703
|
+
// src/destinations/types.ts
|
|
10704
|
+
var PUT_FLAGS = [
|
|
10705
|
+
"repo",
|
|
10706
|
+
"env",
|
|
10707
|
+
"app",
|
|
10708
|
+
"project",
|
|
10709
|
+
"worker",
|
|
10710
|
+
"secret",
|
|
10711
|
+
"aws-region",
|
|
10712
|
+
"locations",
|
|
10713
|
+
"namespace",
|
|
10714
|
+
"context",
|
|
10715
|
+
"config"
|
|
10716
|
+
];
|
|
10717
|
+
var keyName = (s) => s.length <= 80 ? s : `${s.slice(0, 79)}…`;
|
|
10718
|
+
|
|
10719
|
+
// src/destinations/aws.ts
|
|
10720
|
+
var AWS = {
|
|
10721
|
+
cmd: "aws",
|
|
10722
|
+
install: "Install the AWS CLI and configure it: https://aws.amazon.com/cli/, then `aws configure` or `aws sso login`.",
|
|
10723
|
+
login: "Check `aws sts get-caller-identity` and that you may manage Secrets Manager secrets in that region."
|
|
10724
|
+
};
|
|
10725
|
+
function target(region, secret) {
|
|
10726
|
+
return {
|
|
10727
|
+
livesIn: `aws:${region}/${secret}`,
|
|
10728
|
+
keyName: keyName(`aws: ${region} / ${secret}`),
|
|
10729
|
+
params: { region, secret }
|
|
10730
|
+
};
|
|
10731
|
+
}
|
|
10732
|
+
var FROM_STDIN = ["--secret-string", "file:///dev/stdin"];
|
|
10733
|
+
var aws = {
|
|
10734
|
+
id: "aws",
|
|
10735
|
+
label: "AWS Secrets Manager",
|
|
10736
|
+
flags: ["aws-region", "secret"],
|
|
10737
|
+
async resolve(f, io) {
|
|
10738
|
+
let region = f["aws-region"] ?? io.env.AWS_REGION ?? io.env.AWS_DEFAULT_REGION;
|
|
10739
|
+
if (!region)
|
|
10740
|
+
region = (await probe(io, AWS, ["configure", "get", "region"])).stdout.trim();
|
|
10741
|
+
if (!region)
|
|
10742
|
+
throw new CliError("bad_argument", "Which AWS region?", "Pass --aws-region, e.g. us-east-1.");
|
|
10743
|
+
const secret = safeValue("secret", f.secret ?? "scaledthought/api-key", { slash: true });
|
|
10744
|
+
return target(safeValue("aws-region", region), secret);
|
|
10745
|
+
},
|
|
10746
|
+
parse(rest) {
|
|
10747
|
+
const i = rest.indexOf("/");
|
|
10748
|
+
const region = rest.slice(0, i);
|
|
10749
|
+
const secret = rest.slice(i + 1);
|
|
10750
|
+
return i > 0 && isSafeValue(region) && isSafeValue(secret, { slash: true }) ? target(region, secret) : null;
|
|
10751
|
+
},
|
|
10752
|
+
async put(t, secret, io) {
|
|
10753
|
+
const { region, secret: name } = t.params;
|
|
10754
|
+
const exists = await probe(io, AWS, ["secretsmanager", "describe-secret", "--secret-id", name, "--region", region]);
|
|
10755
|
+
const args = exists.code === 0 ? ["secretsmanager", "put-secret-value", "--secret-id", name, ...FROM_STDIN, "--region", region] : ["secretsmanager", "create-secret", "--name", name, ...FROM_STDIN, "--region", region];
|
|
10756
|
+
const shown = `aws ${args.join(" ")}`;
|
|
10757
|
+
await exec(io, AWS, args, { stdin: secret, shown });
|
|
10758
|
+
return `${shown} (stdin)`;
|
|
10759
|
+
},
|
|
10760
|
+
next: (t) => `Read secret ${t.params.secret} in ${t.params.region} (AWSCURRENT); cached readers see it on refresh.`
|
|
10761
|
+
};
|
|
10762
|
+
|
|
10763
|
+
// src/destinations/cloudflare.ts
|
|
10764
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
10765
|
+
import { join as join6 } from "node:path";
|
|
10766
|
+
var WRANGLER = {
|
|
10767
|
+
cmd: "wrangler",
|
|
10768
|
+
install: "Install Wrangler and log in: npm i -g wrangler && wrangler login",
|
|
10769
|
+
login: "Check `wrangler whoami` (log in with `wrangler login`) and the Worker name."
|
|
10770
|
+
};
|
|
10771
|
+
function target2(worker, env) {
|
|
10772
|
+
return {
|
|
10773
|
+
livesIn: `cloudflare:${worker}${env ? `/${env}` : ""}`,
|
|
10774
|
+
keyName: keyName(`cloudflare: ${worker}${env ? ` / ${env}` : ""}`),
|
|
10775
|
+
params: { worker, ...env ? { env } : {} }
|
|
10776
|
+
};
|
|
10777
|
+
}
|
|
10778
|
+
function workerName(cwd) {
|
|
10779
|
+
for (const f of ["wrangler.toml", "wrangler.json", "wrangler.jsonc"]) {
|
|
10780
|
+
const p = join6(cwd, f);
|
|
10781
|
+
if (!existsSync5(p))
|
|
10782
|
+
continue;
|
|
10783
|
+
const text = readFileSync7(p, "utf8");
|
|
10784
|
+
const m = f.endsWith(".toml") ? /^\s*name\s*=\s*["']([^"']+)["']/m.exec(text) : /"name"\s*:\s*"([^"]+)"/.exec(text);
|
|
10785
|
+
if (m)
|
|
10786
|
+
return m[1];
|
|
10787
|
+
}
|
|
10788
|
+
return;
|
|
10789
|
+
}
|
|
10790
|
+
var cloudflare = {
|
|
10791
|
+
id: "cloudflare",
|
|
10792
|
+
label: "Cloudflare Workers",
|
|
10793
|
+
flags: ["worker", "env"],
|
|
10794
|
+
async resolve(f, io) {
|
|
10795
|
+
const worker = f.worker ?? workerName(io.cwd);
|
|
10796
|
+
if (!worker)
|
|
10797
|
+
throw new CliError("bad_argument", "Which Worker?", "Pass --worker <name> (or run where wrangler.toml is).");
|
|
10798
|
+
return target2(safeValue("worker", worker), f.env ? safeValue("env", f.env) : undefined);
|
|
10799
|
+
},
|
|
10800
|
+
parse(rest) {
|
|
10801
|
+
const [worker, env, ...more] = rest.split("/");
|
|
10802
|
+
if (!isSafeValue(worker) || more.length || env && !isSafeValue(env))
|
|
10803
|
+
return null;
|
|
10804
|
+
return target2(worker, env || undefined);
|
|
10805
|
+
},
|
|
10806
|
+
async put(t, secret, io) {
|
|
10807
|
+
const args = ["secret", "put", "ST_API_KEY", "--name", t.params.worker];
|
|
10808
|
+
if (t.params.env)
|
|
10809
|
+
args.push("--env", t.params.env);
|
|
10810
|
+
const shown = `wrangler ${args.join(" ")}`;
|
|
10811
|
+
await exec(io, WRANGLER, args, { stdin: secret, shown });
|
|
10812
|
+
return `${shown} (stdin)`;
|
|
10813
|
+
},
|
|
10814
|
+
next: () => "The Worker reads env.ST_API_KEY; Cloudflare deploys the new secret at once."
|
|
10815
|
+
};
|
|
10816
|
+
|
|
10817
|
+
// src/destinations/doppler.ts
|
|
10818
|
+
var DOPPLER = {
|
|
10819
|
+
cmd: "doppler",
|
|
10820
|
+
install: "Install the Doppler CLI and log in: https://docs.doppler.com/docs/install-cli, then `doppler login`.",
|
|
10821
|
+
login: "Check `doppler me` and the project and config names (`doppler setup` links a directory)."
|
|
10822
|
+
};
|
|
10823
|
+
function target3(project, config) {
|
|
10824
|
+
return {
|
|
10825
|
+
livesIn: `doppler:${project}/${config}`,
|
|
10826
|
+
keyName: keyName(`doppler: ${project} / ${config}`),
|
|
10827
|
+
params: { project, config }
|
|
10828
|
+
};
|
|
10829
|
+
}
|
|
10830
|
+
var doppler = {
|
|
10831
|
+
id: "doppler",
|
|
10832
|
+
label: "Doppler",
|
|
10833
|
+
flags: ["project", "config"],
|
|
10834
|
+
async resolve(f, io) {
|
|
10835
|
+
const get = async (what) => (await probe(io, DOPPLER, ["configure", "get", what, "--plain"])).stdout.trim();
|
|
10836
|
+
const project = f.project ?? await get("project");
|
|
10837
|
+
const config = f.config ?? await get("config");
|
|
10838
|
+
if (!project || !config)
|
|
10839
|
+
throw new CliError("bad_argument", "Which Doppler project and config?", "Pass --project and --config (or run `doppler setup` here).");
|
|
10840
|
+
return target3(safeValue("project", project), safeValue("config", config));
|
|
10841
|
+
},
|
|
10842
|
+
parse(rest) {
|
|
10843
|
+
const [project, config, ...more] = rest.split("/");
|
|
10844
|
+
return isSafeValue(project) && isSafeValue(config) && !more.length ? target3(project, config) : null;
|
|
10845
|
+
},
|
|
10846
|
+
async put(t, secret, io) {
|
|
10847
|
+
const args = ["secrets", "set", "ST_API_KEY", "--project", t.params.project, "--config", t.params.config];
|
|
10848
|
+
const shown = `doppler ${args.join(" ")}`;
|
|
10849
|
+
await exec(io, DOPPLER, args, { stdin: secret, shown });
|
|
10850
|
+
return `${shown} (stdin)`;
|
|
10851
|
+
},
|
|
10852
|
+
next: () => "Apps started with `doppler run` get it on their next start."
|
|
10853
|
+
};
|
|
10854
|
+
|
|
10855
|
+
// src/destinations/fly.ts
|
|
10856
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
10857
|
+
import { join as join7 } from "node:path";
|
|
10858
|
+
var FLY = {
|
|
10859
|
+
cmd: "fly",
|
|
10860
|
+
install: "Install flyctl and log in: https://fly.io/docs/flyctl/install/, then `fly auth login`.",
|
|
10861
|
+
login: "Check `fly auth whoami` (log in with `fly auth login`) and the app name."
|
|
10862
|
+
};
|
|
10863
|
+
var FLYCTL = { ...FLY, cmd: "flyctl" };
|
|
10864
|
+
var target4 = (app) => ({ livesIn: `fly:${app}`, keyName: keyName(`fly: ${app}`), params: { app } });
|
|
10865
|
+
function appFromToml(cwd) {
|
|
10866
|
+
const f = join7(cwd, "fly.toml");
|
|
10867
|
+
if (!existsSync6(f))
|
|
10868
|
+
return;
|
|
10869
|
+
return /^\s*app\s*=\s*["']([^"']+)["']/m.exec(readFileSync8(f, "utf8"))?.[1];
|
|
10870
|
+
}
|
|
10871
|
+
async function run(io, args, stdin, shown) {
|
|
10872
|
+
try {
|
|
10873
|
+
await exec(io, FLY, args, { stdin, shown });
|
|
10874
|
+
} catch (e) {
|
|
10875
|
+
if (e.code !== "destination_cli_missing")
|
|
10876
|
+
throw e;
|
|
10877
|
+
await exec(io, FLYCTL, args, { stdin, shown });
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
var fly = {
|
|
10881
|
+
id: "fly",
|
|
10882
|
+
label: "Fly.io",
|
|
10883
|
+
flags: ["app"],
|
|
10884
|
+
async resolve(f, io) {
|
|
10885
|
+
const app = f.app ?? appFromToml(io.cwd);
|
|
10886
|
+
if (!app)
|
|
10887
|
+
throw new CliError("bad_argument", "Which Fly app?", "Pass --app <name> (or run where fly.toml is).");
|
|
10888
|
+
return target4(safeValue("app", app));
|
|
10889
|
+
},
|
|
10890
|
+
parse: (rest) => isSafeValue(rest) ? target4(rest) : null,
|
|
10891
|
+
async put(t, secret, io) {
|
|
10892
|
+
const args = ["secrets", "import", "-a", t.params.app];
|
|
10893
|
+
const shown = `fly ${args.join(" ")}`;
|
|
10894
|
+
await run(io, args, `ST_API_KEY=${secret}
|
|
10895
|
+
`, shown);
|
|
10896
|
+
return `${shown} (stdin)`;
|
|
10897
|
+
},
|
|
10898
|
+
next: () => "Fly restarts the app's machines with the new secret."
|
|
10899
|
+
};
|
|
10900
|
+
|
|
10901
|
+
// src/destinations/gcp.ts
|
|
10902
|
+
var GCLOUD = {
|
|
10903
|
+
cmd: "gcloud",
|
|
10904
|
+
install: "Install the Google Cloud CLI and log in: https://cloud.google.com/sdk/docs/install, then `gcloud auth login`.",
|
|
10905
|
+
login: "Check `gcloud auth list` and that you may manage Secret Manager secrets in the project."
|
|
10906
|
+
};
|
|
10907
|
+
var SECRET_RE = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,254}$/;
|
|
10908
|
+
function target5(project, secret, locations) {
|
|
10909
|
+
return {
|
|
10910
|
+
livesIn: `gcp:${project}/${secret}`,
|
|
10911
|
+
keyName: keyName(`gcp: ${project} / ${secret}`),
|
|
10912
|
+
params: { project, secret, ...locations ? { locations } : {} }
|
|
10913
|
+
};
|
|
10914
|
+
}
|
|
10915
|
+
var gcp = {
|
|
10916
|
+
id: "gcp",
|
|
10917
|
+
label: "Google Cloud Secret Manager",
|
|
10918
|
+
flags: ["project", "secret", "locations"],
|
|
10919
|
+
async resolve(f, io) {
|
|
10920
|
+
let project = f.project;
|
|
10921
|
+
if (!project) {
|
|
10922
|
+
project = (await exec(io, GCLOUD, ["config", "get-value", "project"])).trim();
|
|
10923
|
+
if (!project || project === "(unset)")
|
|
10924
|
+
throw new CliError("bad_argument", "No Google Cloud project is set.", "Pass --project <id>.");
|
|
10925
|
+
}
|
|
10926
|
+
const secret = f.secret ?? "st-api-key";
|
|
10927
|
+
if (!SECRET_RE.test(secret))
|
|
10928
|
+
throw new CliError("bad_argument", "--secret may use letters, digits, _ and - only (Secret Manager ids).");
|
|
10929
|
+
return target5(safeValue("project", project), secret, f.locations ? safeValue("locations", f.locations.replace(/\s/g, "")) : undefined);
|
|
10930
|
+
},
|
|
10931
|
+
parse(rest) {
|
|
10932
|
+
const [project, secret, ...more] = rest.split("/");
|
|
10933
|
+
return isSafeValue(project) && secret && SECRET_RE.test(secret) && !more.length ? target5(project, secret) : null;
|
|
10934
|
+
},
|
|
10935
|
+
async put(t, secret, io) {
|
|
10936
|
+
const { project, secret: name } = t.params;
|
|
10937
|
+
const exists = await probe(io, GCLOUD, ["secrets", "describe", name, "--project", project, "--format=value(name)"]);
|
|
10938
|
+
const args = exists.code === 0 ? ["secrets", "versions", "add", name, "--project", project, "--data-file=-"] : [
|
|
10939
|
+
"secrets",
|
|
10940
|
+
"create",
|
|
10941
|
+
name,
|
|
10942
|
+
"--project",
|
|
10943
|
+
project,
|
|
10944
|
+
"--data-file=-",
|
|
10945
|
+
...t.params.locations ? ["--replication-policy=user-managed", `--locations=${t.params.locations}`] : ["--replication-policy=automatic"]
|
|
10946
|
+
];
|
|
10947
|
+
const shown = `gcloud ${args.join(" ")}`;
|
|
10948
|
+
await exec(io, GCLOUD, args, { stdin: secret, shown });
|
|
10949
|
+
return `${shown} (stdin)`;
|
|
10950
|
+
},
|
|
10951
|
+
next: (t) => `Read secret ${t.params.secret}, version latest (anything pinned to a version needs updating).`
|
|
10952
|
+
};
|
|
10953
|
+
|
|
10954
|
+
// src/destinations/github.ts
|
|
10955
|
+
var GH = {
|
|
10956
|
+
cmd: "gh",
|
|
10957
|
+
install: "Install the GitHub CLI and log in: https://cli.github.com, then `gh auth login`.",
|
|
10958
|
+
login: "Check `gh auth status` (log in with `gh auth login`) and that you can manage the repo's secrets."
|
|
10959
|
+
};
|
|
10960
|
+
var APPS = { actions: "Actions", codespaces: "Codespaces", dependabot: "Dependabot" };
|
|
10961
|
+
var REPO_RE = /^[A-Za-z0-9_.][A-Za-z0-9_.-]*\/[A-Za-z0-9_.-]+$/;
|
|
10962
|
+
var isApp = (v) => v !== undefined && Object.hasOwn(APPS, v);
|
|
10963
|
+
function target6(repo, app, env) {
|
|
10964
|
+
return {
|
|
10965
|
+
livesIn: `github:${repo}/${app}${env ? `/${env}` : ""}`,
|
|
10966
|
+
keyName: keyName(`github: ${repo} (${APPS[app]}${env ? `, ${env}` : ""})`),
|
|
10967
|
+
params: { repo, app, ...env ? { env } : {} }
|
|
10968
|
+
};
|
|
10969
|
+
}
|
|
10970
|
+
var github = {
|
|
10971
|
+
id: "github",
|
|
10972
|
+
label: "GitHub",
|
|
10973
|
+
flags: ["repo", "app", "env"],
|
|
10974
|
+
async resolve(f, io) {
|
|
10975
|
+
const app = f.app ?? "actions";
|
|
10976
|
+
if (!isApp(app))
|
|
10977
|
+
throw new CliError("bad_argument", "--app must be actions, codespaces or dependabot.");
|
|
10978
|
+
if (f.env && app !== "actions")
|
|
10979
|
+
throw new CliError("bad_argument", "--env (a deployment environment) works only with --app actions.");
|
|
10980
|
+
const repo = f.repo ?? (await exec(io, GH, ["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"])).trim();
|
|
10981
|
+
if (!REPO_RE.test(repo))
|
|
10982
|
+
throw new CliError("bad_argument", "--repo must look like owner/name.", "Pass --repo owner/name.");
|
|
10983
|
+
return target6(repo, app, f.env ? safeValue("env", f.env) : undefined);
|
|
10984
|
+
},
|
|
10985
|
+
parse(rest) {
|
|
10986
|
+
const [owner, name, app, env, ...more] = rest.split("/");
|
|
10987
|
+
if (!owner || !name || !isApp(app) || more.length || !REPO_RE.test(`${owner}/${name}`))
|
|
10988
|
+
return null;
|
|
10989
|
+
if (env && !isSafeValue(env))
|
|
10990
|
+
return null;
|
|
10991
|
+
return target6(`${owner}/${name}`, app, env || undefined);
|
|
10992
|
+
},
|
|
10993
|
+
async put(t, secret, io) {
|
|
10994
|
+
const args = ["secret", "set", "ST_API_KEY", "--repo", t.params.repo];
|
|
10995
|
+
if (t.params.app !== "actions")
|
|
10996
|
+
args.push("--app", t.params.app);
|
|
10997
|
+
if (t.params.env)
|
|
10998
|
+
args.push("--env", t.params.env);
|
|
10999
|
+
const shown = `gh ${args.join(" ")}`;
|
|
11000
|
+
await exec(io, GH, args, { stdin: secret, shown });
|
|
11001
|
+
return `${shown} (stdin)`;
|
|
11002
|
+
},
|
|
11003
|
+
next: () => "Workflows read it as secrets.ST_API_KEY from their next run."
|
|
11004
|
+
};
|
|
11005
|
+
|
|
11006
|
+
// src/destinations/k8s.ts
|
|
11007
|
+
var KUBECTL = {
|
|
11008
|
+
cmd: "kubectl",
|
|
11009
|
+
install: "Install kubectl and point it at your cluster: https://kubernetes.io/docs/tasks/tools/",
|
|
11010
|
+
login: "Check `kubectl config current-context` and that you may create Secrets in the namespace."
|
|
11011
|
+
};
|
|
11012
|
+
var SECRET_RE2 = /^[a-z0-9]([-a-z0-9.]{0,251}[a-z0-9])?$/;
|
|
11013
|
+
function target7(context, namespace, secret) {
|
|
11014
|
+
return {
|
|
11015
|
+
livesIn: `k8s:${context}/${namespace}/${secret}`,
|
|
11016
|
+
keyName: keyName(`k8s: ${namespace}/${secret} (${context})`),
|
|
11017
|
+
params: { context, namespace, secret }
|
|
11018
|
+
};
|
|
11019
|
+
}
|
|
11020
|
+
var k8s = {
|
|
11021
|
+
id: "k8s",
|
|
11022
|
+
label: "Kubernetes",
|
|
11023
|
+
flags: ["context", "namespace", "secret"],
|
|
11024
|
+
async resolve(f, io) {
|
|
11025
|
+
const context = f.context ?? (await exec(io, KUBECTL, ["config", "current-context"])).trim();
|
|
11026
|
+
if (!context)
|
|
11027
|
+
throw new CliError("bad_argument", "No kubectl context is set.", "Pass --context <name>.");
|
|
11028
|
+
let namespace = f.namespace;
|
|
11029
|
+
if (!namespace) {
|
|
11030
|
+
const r = await probe(io, KUBECTL, ["config", "view", "--minify", "-o", "jsonpath={..namespace}"]);
|
|
11031
|
+
namespace = r.code === 0 && r.stdout.trim() ? r.stdout.trim() : "default";
|
|
11032
|
+
}
|
|
11033
|
+
const secret = f.secret ?? "scaledthought";
|
|
11034
|
+
if (!SECRET_RE2.test(secret))
|
|
11035
|
+
throw new CliError("bad_argument", "--secret must be a lowercase DNS name, e.g. scaledthought.");
|
|
11036
|
+
return target7(safeValue("context", context, { slash: true }), safeValue("namespace", namespace), secret);
|
|
11037
|
+
},
|
|
11038
|
+
parse(rest) {
|
|
11039
|
+
const parts = rest.split("/");
|
|
11040
|
+
if (parts.length < 3)
|
|
11041
|
+
return null;
|
|
11042
|
+
const secret = parts.pop();
|
|
11043
|
+
const namespace = parts.pop();
|
|
11044
|
+
const context = parts.join("/");
|
|
11045
|
+
if (!isSafeValue(context, { slash: true }) || !isSafeValue(namespace) || !SECRET_RE2.test(secret))
|
|
11046
|
+
return null;
|
|
11047
|
+
return target7(context, namespace, secret);
|
|
11048
|
+
},
|
|
11049
|
+
async put(t, secret, io) {
|
|
11050
|
+
const where = ["--namespace", t.params.namespace, "--context", t.params.context];
|
|
11051
|
+
const create = ["create", "secret", "generic", t.params.secret, "--from-file=ST_API_KEY=/dev/stdin"];
|
|
11052
|
+
const manifest = await exec(io, KUBECTL, [...create, "--dry-run=client", "-o", "yaml", ...where], {
|
|
11053
|
+
stdin: secret,
|
|
11054
|
+
shown: `kubectl create secret generic ${t.params.secret}`
|
|
11055
|
+
});
|
|
11056
|
+
const apply = ["apply", "--server-side", "--force-conflicts", "--field-manager=scaledthought", "-f", "-", ...where];
|
|
11057
|
+
await exec(io, KUBECTL, apply, {
|
|
11058
|
+
stdin: manifest,
|
|
11059
|
+
secrets: [secret, Buffer.from(secret).toString("base64")],
|
|
11060
|
+
shown: "kubectl apply --server-side -f -"
|
|
11061
|
+
});
|
|
11062
|
+
return `kubectl ${create.join(" ")} --dry-run=client -o yaml | kubectl apply --server-side -f - (stdin)`;
|
|
11063
|
+
},
|
|
11064
|
+
next: (t) => `Pods read Secret ${t.params.secret} when they start: restart the ones that use it (kubectl rollout restart deployment/<name> -n ${t.params.namespace}).`
|
|
11065
|
+
};
|
|
11066
|
+
|
|
11067
|
+
// src/destinations/vercel.ts
|
|
11068
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
11069
|
+
import { join as join8 } from "node:path";
|
|
11070
|
+
var VERCEL = {
|
|
11071
|
+
cmd: "vercel",
|
|
11072
|
+
install: "Install the Vercel CLI and log in: npm i -g vercel && vercel login",
|
|
11073
|
+
login: "Check `vercel whoami` (log in with `vercel login`) and that this directory is linked (`vercel link`)."
|
|
11074
|
+
};
|
|
11075
|
+
function target8(project, env) {
|
|
11076
|
+
return {
|
|
11077
|
+
livesIn: `vercel:${project}/${env}`,
|
|
11078
|
+
keyName: keyName(`vercel: ${project} / ${env}`),
|
|
11079
|
+
params: { project, env }
|
|
11080
|
+
};
|
|
11081
|
+
}
|
|
11082
|
+
function linkedProject(cwd) {
|
|
11083
|
+
const f = join8(cwd, ".vercel", "project.json");
|
|
11084
|
+
if (!existsSync7(f))
|
|
11085
|
+
return null;
|
|
11086
|
+
try {
|
|
11087
|
+
const j = JSON.parse(readFileSync9(f, "utf8"));
|
|
11088
|
+
return { name: j.projectName, id: j.projectId };
|
|
11089
|
+
} catch {
|
|
11090
|
+
return null;
|
|
11091
|
+
}
|
|
11092
|
+
}
|
|
11093
|
+
var NOT_LINKED = "Run this in the app's directory after `vercel link`.";
|
|
11094
|
+
var vercel = {
|
|
11095
|
+
id: "vercel",
|
|
11096
|
+
label: "Vercel",
|
|
11097
|
+
flags: ["project", "env"],
|
|
11098
|
+
async resolve(f, io) {
|
|
11099
|
+
const env = safeValue("env", f.env ?? "production");
|
|
11100
|
+
const linked = linkedProject(io.cwd);
|
|
11101
|
+
if (!linked)
|
|
11102
|
+
throw new CliError("not_linked", "This directory isn't linked to a Vercel project.", NOT_LINKED);
|
|
11103
|
+
const project = f.project ?? linked.name ?? linked.id;
|
|
11104
|
+
if (!project)
|
|
11105
|
+
throw new CliError("not_linked", "Can't tell which Vercel project this is.", "Pass --project.");
|
|
11106
|
+
if (f.project && linked.name && f.project !== linked.name)
|
|
11107
|
+
throw new CliError("not_linked", `This directory is linked to Vercel project "${linked.name}", not "${f.project}".`, NOT_LINKED);
|
|
11108
|
+
return target8(safeValue("project", project), env);
|
|
11109
|
+
},
|
|
11110
|
+
parse(rest) {
|
|
11111
|
+
const [project, env, ...more] = rest.split("/");
|
|
11112
|
+
return isSafeValue(project) && isSafeValue(env) && !more.length ? target8(project, env) : null;
|
|
11113
|
+
},
|
|
11114
|
+
async put(t, secret, io, mode) {
|
|
11115
|
+
const linked = linkedProject(io.cwd);
|
|
11116
|
+
if (!linked || linked.name !== t.params.project && linked.id !== t.params.project)
|
|
11117
|
+
throw new CliError("not_linked", `Vercel project "${t.params.project}" isn't the one linked in this directory.`, `Run \`keys rotate <id> --push\` in the directory linked to "${t.params.project}".`);
|
|
11118
|
+
if (mode === "replace")
|
|
11119
|
+
await probe(io, VERCEL, ["env", "rm", "ST_API_KEY", t.params.env, "--yes"]);
|
|
11120
|
+
const args = ["env", "add", "ST_API_KEY", t.params.env];
|
|
11121
|
+
const shown = `vercel ${args.join(" ")}`;
|
|
11122
|
+
await exec(io, VERCEL, args, { stdin: secret, shown });
|
|
11123
|
+
return `${shown} (stdin)`;
|
|
11124
|
+
},
|
|
11125
|
+
next: (t) => `Vercel reads the new value on the next deployment: ${t.params.env === "production" ? "vercel --prod" : "vercel"}`
|
|
11126
|
+
};
|
|
11127
|
+
|
|
11128
|
+
// src/destinations/index.ts
|
|
11129
|
+
var DESTINATIONS = Object.fromEntries([github, vercel, fly, cloudflare, gcp, aws, k8s, doppler].map((d) => [d.id, d]));
|
|
11130
|
+
function destination(id) {
|
|
11131
|
+
const d = Object.hasOwn(DESTINATIONS, id) ? DESTINATIONS[id] : undefined;
|
|
11132
|
+
if (!d)
|
|
11133
|
+
throw new CliError("unknown_destination", `Unknown destination "${id}".`, `Use one of: ${Object.keys(DESTINATIONS).join(", ")}.`);
|
|
11134
|
+
return d;
|
|
11135
|
+
}
|
|
11136
|
+
function fromLivesIn(livesIn) {
|
|
11137
|
+
if (!livesIn)
|
|
11138
|
+
return null;
|
|
11139
|
+
const i = livesIn.indexOf(":");
|
|
11140
|
+
if (i <= 0)
|
|
11141
|
+
return null;
|
|
11142
|
+
const id = livesIn.slice(0, i);
|
|
11143
|
+
const dest = Object.hasOwn(DESTINATIONS, id) ? DESTINATIONS[id] : undefined;
|
|
11144
|
+
const target = dest?.parse(livesIn.slice(i + 1));
|
|
11145
|
+
return dest && target ? { dest, target } : null;
|
|
11146
|
+
}
|
|
11147
|
+
|
|
11148
|
+
// src/everywhere.ts
|
|
11149
|
+
import { chmodSync as chmodSync4, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11150
|
+
import { homedir, hostname as hostname2 } from "node:os";
|
|
11151
|
+
import { dirname as dirname2, join as join9 } from "node:path";
|
|
11152
|
+
var io = (ctx) => ({ run: ctx.runner ?? spawnRunner, cwd: ctx.cwd, env: ctx.env });
|
|
11153
|
+
async function mintChild(ctx, o) {
|
|
11154
|
+
const k = await api(ctx, "POST", "/v1/keys", {
|
|
11155
|
+
body: {
|
|
11156
|
+
name: o.name,
|
|
11157
|
+
child: true,
|
|
11158
|
+
lives_in: o.livesIn,
|
|
11159
|
+
daily_budget_usd: o.budgetUsd,
|
|
11160
|
+
scopes: o.scopes,
|
|
11161
|
+
expires_in: o.expiresIn
|
|
11162
|
+
}
|
|
11163
|
+
});
|
|
11164
|
+
if (!k.api_key || !KEY_RE.test(k.api_key))
|
|
11165
|
+
throw new CliError("bad_response", "The server didn't return the new key.", "Retry; if it persists, report it.");
|
|
11166
|
+
return k;
|
|
11167
|
+
}
|
|
11168
|
+
async function revokeMinted(ctx, id) {
|
|
11169
|
+
return api(ctx, "DELETE", `/v1/keys/${encodeURIComponent(id)}`).then(() => true, () => false);
|
|
11170
|
+
}
|
|
11171
|
+
async function failAfterMint(ctx, keyId, e) {
|
|
11172
|
+
const revoked = await revokeMinted(ctx, keyId);
|
|
11173
|
+
const err = e instanceof CliError ? e : new CliError("destination_failed", "Storing the key failed.", "Retry; if it persists, report it.");
|
|
11174
|
+
throw new CliError(err.code, `${err.message} ${revoked ? "The new key was revoked; nothing else changed." : `Revoking the new key ${keyId} failed: run \`keys revoke ${keyId}\`.`}`, err.hint, { ...err.extra, key_id: keyId, key_revoked: revoked });
|
|
11175
|
+
}
|
|
11176
|
+
function checkFlags(dest, flags) {
|
|
11177
|
+
const extra = Object.keys(flags).filter((f) => flags[f] !== undefined && !dest.flags.includes(f));
|
|
11178
|
+
if (extra.length)
|
|
11179
|
+
throw new CliError("usage_error", `keys put ${dest.id} doesn't take --${extra.join(", --")}.`, `It takes: ${dest.flags.map((f) => `--${f}`).join(", ") || "no destination flags"}.`);
|
|
11180
|
+
}
|
|
11181
|
+
async function keysPut(ctx, destId, o) {
|
|
11182
|
+
const dest = destination(destId);
|
|
11183
|
+
checkFlags(dest, o.flags);
|
|
11184
|
+
const { region } = resolveKey(ctx);
|
|
11185
|
+
const target = await dest.resolve(o.flags, io(ctx));
|
|
11186
|
+
const k = await mintChild(ctx, {
|
|
11187
|
+
name: o.name ?? target.keyName,
|
|
11188
|
+
livesIn: target.livesIn,
|
|
11189
|
+
budgetUsd: o.budgetUsd,
|
|
11190
|
+
scopes: o.scopes,
|
|
11191
|
+
expiresIn: o.expiresIn
|
|
11192
|
+
});
|
|
11193
|
+
let storedWith;
|
|
11194
|
+
try {
|
|
11195
|
+
storedWith = await dest.put(target, k.api_key, io(ctx), "new");
|
|
11196
|
+
} catch (e) {
|
|
11197
|
+
return failAfterMint(ctx, k.id, e);
|
|
11198
|
+
}
|
|
11199
|
+
return {
|
|
11200
|
+
key_name: k.name,
|
|
11201
|
+
key_id: k.id,
|
|
11202
|
+
key_prefix: k.prefix,
|
|
11203
|
+
lives_in: target.livesIn,
|
|
11204
|
+
stored_with: storedWith,
|
|
11205
|
+
daily_budget_usd: k.daily_budget_usd,
|
|
11206
|
+
base_url: inferenceUrl(region, ctx.env),
|
|
11207
|
+
next: `${dest.next?.(target) ?? ""} The app reads ST_API_KEY; its base URL is ${inferenceUrl(region, ctx.env)} (not a secret: set ST_BASE_URL as a plain variable if your code uses it).`.trim()
|
|
11208
|
+
};
|
|
11209
|
+
}
|
|
11210
|
+
async function keysRotatePush(ctx, o) {
|
|
11211
|
+
const grace = o.grace ?? "24h";
|
|
11212
|
+
const { data } = await api(ctx, "GET", "/v1/keys");
|
|
11213
|
+
let keys;
|
|
11214
|
+
if (o.all)
|
|
11215
|
+
keys = data.filter((k) => k.status === "active");
|
|
11216
|
+
else {
|
|
11217
|
+
const k = data.find((x) => x.id === o.id);
|
|
11218
|
+
if (!k)
|
|
11219
|
+
throw new CliError("key_not_found", `No key ${o.id} on this account.`, "See `keys list`.");
|
|
11220
|
+
keys = [k];
|
|
11221
|
+
}
|
|
11222
|
+
const rotated = [];
|
|
11223
|
+
const skipped = [];
|
|
11224
|
+
const failed = [];
|
|
11225
|
+
const next = new Set;
|
|
11226
|
+
for (const k of keys) {
|
|
11227
|
+
const found = fromLivesIn(k.lives_in);
|
|
11228
|
+
if (!found) {
|
|
11229
|
+
const tool = /^tool:([a-z0-9-]+):/.exec(k.lives_in ?? "")?.[1];
|
|
11230
|
+
skipped.push({
|
|
11231
|
+
name: k.name,
|
|
11232
|
+
key_id: k.id,
|
|
11233
|
+
lives_in: k.lives_in ?? null,
|
|
11234
|
+
reason: !k.lives_in ? "no lives_in (not put anywhere by keys put)" : tool ? `a tool's config: run \`connect ${tool}\` again on that machine, then revoke this key` : k.lives_in.startsWith("env:") ? "a folder's ./.env (a project key from init, login or use): rotate it from that folder with `keys rotate`" : "lives_in isn't a destination keys put knows"
|
|
11235
|
+
});
|
|
11236
|
+
continue;
|
|
11237
|
+
}
|
|
11238
|
+
if (k.status !== "active") {
|
|
11239
|
+
skipped.push({ name: k.name, key_id: k.id, lives_in: k.lives_in ?? null, reason: `key is ${k.status}` });
|
|
11240
|
+
continue;
|
|
11241
|
+
}
|
|
11242
|
+
let r;
|
|
11243
|
+
try {
|
|
11244
|
+
r = await api(ctx, "POST", `/v1/keys/${encodeURIComponent(k.id)}/rotate`, { body: { grace } });
|
|
11245
|
+
} catch (e) {
|
|
11246
|
+
failed.push({ name: k.name, key_id: k.id, error: errorOf(e) });
|
|
11247
|
+
continue;
|
|
11248
|
+
}
|
|
11249
|
+
if (!r.api_key) {
|
|
11250
|
+
failed.push({ name: k.name, key_id: r.id, error: errorOf(null) });
|
|
11251
|
+
continue;
|
|
11252
|
+
}
|
|
11253
|
+
const stops = { old_key_stops: `in ${grace}`, old_key_expires_at: r.rotated_from?.expires_at ?? "" };
|
|
11254
|
+
try {
|
|
11255
|
+
const pushed = await found.dest.put(found.target, r.api_key, io(ctx), "replace");
|
|
11256
|
+
rotated.push({ name: r.name, key_id: r.id, pushed, ...stops });
|
|
11257
|
+
const n = found.dest.next?.(found.target);
|
|
11258
|
+
if (n)
|
|
11259
|
+
next.add(n);
|
|
11260
|
+
} catch (e) {
|
|
11261
|
+
const err = errorOf(e);
|
|
11262
|
+
failed.push({
|
|
11263
|
+
name: r.name,
|
|
11264
|
+
key_id: r.id,
|
|
11265
|
+
...stops,
|
|
11266
|
+
error: {
|
|
11267
|
+
...err,
|
|
11268
|
+
hint: `${err.hint ?? ""} The old key keeps working ${stops.old_key_stops}. Fix that, then run \`keys rotate ${r.id} --push --grace 0\`.`.trim()
|
|
11269
|
+
}
|
|
11270
|
+
});
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
11273
|
+
return {
|
|
11274
|
+
rotated,
|
|
11275
|
+
skipped,
|
|
11276
|
+
...failed.length ? { failed } : {},
|
|
11277
|
+
...next.size ? { next: [...next] } : {}
|
|
11278
|
+
};
|
|
11279
|
+
}
|
|
11280
|
+
function errorOf(e) {
|
|
11281
|
+
if (e instanceof CliError)
|
|
11282
|
+
return { code: e.code, message: e.message, hint: e.hint ?? null };
|
|
11283
|
+
return { code: "internal_error", message: "Unexpected error.", hint: "Retry; if it persists, report it." };
|
|
11284
|
+
}
|
|
11285
|
+
var CONNECT_TOOLS = ["opencode", "aider", "continue", "cursor"];
|
|
11286
|
+
function machineName(raw = hostname2()) {
|
|
11287
|
+
const n = raw.replace(/\.(local|lan|home)$/i, "").replace(/[^A-Za-z0-9.-]/g, "-").slice(0, 40);
|
|
11288
|
+
return n || "this-machine";
|
|
11289
|
+
}
|
|
11290
|
+
async function liveChatModels(ctx) {
|
|
11291
|
+
const { region } = resolveKey(ctx);
|
|
11292
|
+
const res = await api(ctx, "GET", "/v1/models?modality=chat", {
|
|
11293
|
+
auth: false,
|
|
11294
|
+
region
|
|
11295
|
+
}).catch(() => ({ data: [] }));
|
|
11296
|
+
return (res.data ?? []).filter((m) => typeof m.id === "string" && m.live_in_region !== false && (m.modality ?? "chat") === "chat").map((m) => ({ id: m.id, context: typeof m.context === "number" ? m.context : null }));
|
|
11297
|
+
}
|
|
11298
|
+
function writePrivate2(path, text) {
|
|
11299
|
+
mkdirSync3(dirname2(path), { recursive: true, mode: 448 });
|
|
11300
|
+
writeFileSync4(path, text, { mode: 384 });
|
|
11301
|
+
chmodSync4(path, 384);
|
|
11302
|
+
}
|
|
11303
|
+
function readJsonObject(path, what) {
|
|
11304
|
+
if (!existsSync8(path))
|
|
11305
|
+
return {};
|
|
11306
|
+
try {
|
|
11307
|
+
const v = JSON.parse(readFileSync10(path, "utf8"));
|
|
11308
|
+
if (v && typeof v === "object" && !Array.isArray(v))
|
|
11309
|
+
return v;
|
|
11310
|
+
} catch {}
|
|
11311
|
+
throw new CliError("config_unreadable", `${what} isn't plain JSON (comments or a syntax error). Nothing was changed.`, `Fix or move ${path}, then run connect again.`);
|
|
11312
|
+
}
|
|
11313
|
+
var PROVIDER_ID = "scaledthought";
|
|
11314
|
+
async function connect(ctx, tool, o = {}) {
|
|
11315
|
+
if (!CONNECT_TOOLS.includes(tool))
|
|
11316
|
+
throw new CliError("unknown_tool", `Unknown tool "${tool}".`, `Use one of: ${CONNECT_TOOLS.join(", ")}.`);
|
|
11317
|
+
const { region } = resolveKey(ctx);
|
|
11318
|
+
const baseUrl = inferenceUrl(region, ctx.env);
|
|
11319
|
+
const models = await liveChatModels(ctx);
|
|
11320
|
+
const model = o.model ?? models[0]?.id ?? "auto";
|
|
11321
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(model))
|
|
11322
|
+
throw new CliError("bad_argument", "--model isn't a model id.", "Pick one from `models --live`.");
|
|
11323
|
+
const host = machineName();
|
|
11324
|
+
const keyName = `${tool}: ${host}`;
|
|
11325
|
+
const livesIn = `tool:${tool}:${host}`;
|
|
11326
|
+
const t = tool;
|
|
11327
|
+
if (t === "cursor" || t === "continue")
|
|
11328
|
+
return instructions(t, baseUrl, model, models, keyName);
|
|
11329
|
+
if (t === "aider") {
|
|
11330
|
+
const existing = readDotEnv(ctx.cwd);
|
|
11331
|
+
if (existing.OPENAI_API_KEY && !KEY_RE.test(existing.OPENAI_API_KEY))
|
|
11332
|
+
throw new CliError("env_conflict", "./.env already has an OPENAI_API_KEY that isn't a ScaledThought key. Nothing was changed.", "Aider reads OPENAI_API_KEY from .env, so this would replace it. Run connect aider in another directory, or remove that line first.");
|
|
11333
|
+
const k = await mintChild(ctx, { name: keyName, livesIn, budgetUsd: o.budgetUsd });
|
|
11334
|
+
try {
|
|
11335
|
+
writeEnv(ctx.cwd, { OPENAI_API_BASE: baseUrl, OPENAI_API_KEY: k.api_key, AIDER_MODEL: `openai/${model}` });
|
|
11336
|
+
ensureGitignored(ctx.cwd);
|
|
11337
|
+
} catch (e) {
|
|
11338
|
+
return failAfterMint(ctx, k.id, e);
|
|
11339
|
+
}
|
|
11340
|
+
return {
|
|
11341
|
+
tool,
|
|
11342
|
+
key_name: k.name,
|
|
11343
|
+
key_prefix: k.prefix,
|
|
11344
|
+
lives_in: livesIn,
|
|
11345
|
+
daily_budget_usd: k.daily_budget_usd,
|
|
11346
|
+
configured: ".env (OPENAI_API_BASE, OPENAI_API_KEY, AIDER_MODEL), mode 0600, git-ignored",
|
|
11347
|
+
model: `openai/${model}`,
|
|
11348
|
+
models: ["auto", ...models.map((m) => m.id)],
|
|
11349
|
+
next: "Run `aider` in this directory. Aider may warn that it doesn't know the model's context window; that's harmless."
|
|
11350
|
+
};
|
|
11351
|
+
}
|
|
11352
|
+
const dataHome = ctx.env.XDG_DATA_HOME || join9(ctx.env.HOME || homedir(), ".local", "share");
|
|
11353
|
+
const authPath = join9(dataHome, "opencode", "auth.json");
|
|
11354
|
+
const configPath = join9(ctx.cwd, "opencode.json");
|
|
11355
|
+
const config = readJsonObject(configPath, "opencode.json");
|
|
11356
|
+
const auth = readJsonObject(authPath, authPath);
|
|
11357
|
+
const providers = config.provider && typeof config.provider === "object" ? config.provider : {};
|
|
11358
|
+
const modelEntries = { auto: { name: "auto (ScaledThought picks per request)" } };
|
|
11359
|
+
for (const m of models)
|
|
11360
|
+
modelEntries[m.id] = {
|
|
11361
|
+
name: m.id,
|
|
11362
|
+
...m.context ? { limit: { context: m.context, output: Math.min(32768, Math.floor(m.context / 2)) } } : {}
|
|
11363
|
+
};
|
|
11364
|
+
if (!modelEntries[model])
|
|
11365
|
+
modelEntries[model] = { name: model };
|
|
11366
|
+
const k = await mintChild(ctx, { name: keyName, livesIn, budgetUsd: o.budgetUsd });
|
|
11367
|
+
try {
|
|
11368
|
+
const next = {
|
|
11369
|
+
...existsSync8(configPath) ? {} : { $schema: "https://opencode.ai/config.json" },
|
|
11370
|
+
...config,
|
|
11371
|
+
provider: {
|
|
11372
|
+
...providers,
|
|
11373
|
+
[PROVIDER_ID]: {
|
|
11374
|
+
npm: "@ai-sdk/openai-compatible",
|
|
11375
|
+
name: "ScaledThought",
|
|
11376
|
+
options: { baseURL: baseUrl },
|
|
11377
|
+
models: modelEntries
|
|
11378
|
+
}
|
|
11379
|
+
},
|
|
11380
|
+
...config.model ? {} : { model: `${PROVIDER_ID}/${model}` }
|
|
11381
|
+
};
|
|
11382
|
+
writePrivate2(configPath, `${JSON.stringify(next, null, 2)}
|
|
11383
|
+
`);
|
|
11384
|
+
writePrivate2(authPath, `${JSON.stringify({ ...auth, [PROVIDER_ID]: { type: "api", key: k.api_key } }, null, 2)}
|
|
11385
|
+
`);
|
|
11386
|
+
} catch (e) {
|
|
11387
|
+
return failAfterMint(ctx, k.id, e);
|
|
11388
|
+
}
|
|
11389
|
+
return {
|
|
11390
|
+
tool,
|
|
11391
|
+
key_name: k.name,
|
|
11392
|
+
key_prefix: k.prefix,
|
|
11393
|
+
lives_in: livesIn,
|
|
11394
|
+
daily_budget_usd: k.daily_budget_usd,
|
|
11395
|
+
configured: `opencode.json (provider ${PROVIDER_ID}, base URL ${baseUrl}) + ${authPath} (key, mode 0600)`,
|
|
11396
|
+
model: `${PROVIDER_ID}/${model}`,
|
|
11397
|
+
models: Object.keys(modelEntries),
|
|
11398
|
+
restart_needed: true,
|
|
11399
|
+
next: `Restart OpenCode in this directory and pick a ${PROVIDER_ID}/… model (or ${PROVIDER_ID}/auto).`
|
|
11400
|
+
};
|
|
11401
|
+
}
|
|
11402
|
+
function instructions(tool, baseUrl, model, models, name) {
|
|
11403
|
+
const ids = ["auto", ...models.map((m) => m.id)];
|
|
11404
|
+
if (tool === "cursor")
|
|
11405
|
+
return {
|
|
11406
|
+
tool,
|
|
11407
|
+
configured: false,
|
|
11408
|
+
key_minted: false,
|
|
11409
|
+
why: "Cursor keeps its OpenAI key in its own settings store, which other programs can't write safely. A human sets it once.",
|
|
11410
|
+
steps_for_human: [
|
|
11411
|
+
`Cursor Settings → Models → API Keys: turn on "Override OpenAI Base URL" and enter ${baseUrl}`,
|
|
11412
|
+
`In your own terminal (not through the agent) run: ${NPX} keys create --child --name "${name}" and paste the key it prints into "OpenAI API Key", then Verify.`,
|
|
11413
|
+
`Add model names under Models: ${ids.join(", ")}`
|
|
11414
|
+
],
|
|
11415
|
+
base_url: baseUrl,
|
|
11416
|
+
models: ids,
|
|
11417
|
+
restart_needed: false
|
|
11418
|
+
};
|
|
11419
|
+
const block = [
|
|
11420
|
+
"models:",
|
|
11421
|
+
` - name: ScaledThought ${model}`,
|
|
11422
|
+
" provider: openai",
|
|
11423
|
+
` model: ${model}`,
|
|
11424
|
+
` apiBase: ${baseUrl}`,
|
|
11425
|
+
` apiKey: \${{ secrets.ST_API_KEY }}`,
|
|
11426
|
+
" defaultCompletionOptions:",
|
|
11427
|
+
" maxTokens: 32768",
|
|
11428
|
+
" roles: [chat, edit, apply]"
|
|
11429
|
+
].join(`
|
|
11430
|
+
`);
|
|
11431
|
+
return {
|
|
11432
|
+
tool,
|
|
11433
|
+
configured: false,
|
|
11434
|
+
key_minted: false,
|
|
11435
|
+
why: "Continue's config.yaml is hand-edited YAML; this CLI won't rewrite it. The block below holds no secret: Continue reads ST_API_KEY from this workspace's .env.",
|
|
11436
|
+
steps_for_human: [
|
|
11437
|
+
"Open ~/.continue/config.yaml (Continue: Open configuration) and add this entry under models:",
|
|
11438
|
+
"Reload Continue and pick the ScaledThought model."
|
|
11439
|
+
],
|
|
11440
|
+
config_yaml: block,
|
|
11441
|
+
base_url: baseUrl,
|
|
11442
|
+
models: ids,
|
|
11443
|
+
restart_needed: false
|
|
11444
|
+
};
|
|
11445
|
+
}
|
|
11446
|
+
function putFlagsFrom(args) {
|
|
11447
|
+
const out = {};
|
|
11448
|
+
for (const f of PUT_FLAGS)
|
|
11449
|
+
if (typeof args[f] === "string")
|
|
11450
|
+
out[f] = args[f];
|
|
11451
|
+
return out;
|
|
11452
|
+
}
|
|
11453
|
+
|
|
10584
11454
|
// src/realtime.ts
|
|
10585
|
-
import { readFileSync as
|
|
11455
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "node:fs";
|
|
10586
11456
|
import { resolve as resolve4 } from "node:path";
|
|
10587
11457
|
var SAMPLE_RATE = 24000;
|
|
10588
11458
|
var MAX_INPUT_SECONDS = 300;
|
|
@@ -10647,7 +11517,7 @@ async function realtime(ctx, o) {
|
|
|
10647
11517
|
throw new CliError("unsupported_runtime", "This command needs a WebSocket client (Node.js 22 or newer).", "Upgrade Node.js to 22+.");
|
|
10648
11518
|
let pcm;
|
|
10649
11519
|
try {
|
|
10650
|
-
pcm = readWav(
|
|
11520
|
+
pcm = readWav(readFileSync11(resolve4(ctx.cwd, o.input)));
|
|
10651
11521
|
} catch (e) {
|
|
10652
11522
|
if (e instanceof CliError)
|
|
10653
11523
|
throw e;
|
|
@@ -10739,7 +11609,7 @@ async function realtime(ctx, o) {
|
|
|
10739
11609
|
});
|
|
10740
11610
|
const audio = Buffer.concat(out);
|
|
10741
11611
|
const path = resolve4(ctx.cwd, o.output);
|
|
10742
|
-
|
|
11612
|
+
writeFileSync5(path, wavOf(audio));
|
|
10743
11613
|
return {
|
|
10744
11614
|
model: o.model,
|
|
10745
11615
|
region,
|
|
@@ -10770,7 +11640,7 @@ function out(data, args) {
|
|
|
10770
11640
|
process.stdout.write(`${JSON.stringify(data, null, wantsJson(args) ? 0 : 2)}
|
|
10771
11641
|
`);
|
|
10772
11642
|
}
|
|
10773
|
-
var
|
|
11643
|
+
var run2 = async (fn, args) => out(await fn(), args);
|
|
10774
11644
|
function region(v) {
|
|
10775
11645
|
if (v === undefined)
|
|
10776
11646
|
return;
|
|
@@ -10818,7 +11688,7 @@ var init2 = defineCommand({
|
|
|
10818
11688
|
description: "Save the account's key in this machine's credential store so other folders reuse it (--no-save to opt out)"
|
|
10819
11689
|
}
|
|
10820
11690
|
},
|
|
10821
|
-
run: ({ args }) =>
|
|
11691
|
+
run: ({ args }) => run2(() => init(makeCtx(), {
|
|
10822
11692
|
region: region(args.region),
|
|
10823
11693
|
name: args.name,
|
|
10824
11694
|
email: args.email,
|
|
@@ -10861,7 +11731,7 @@ var login2 = defineCommand({
|
|
|
10861
11731
|
const t = args["timeout-s"] === undefined ? undefined : num("timeout-s", args["timeout-s"]);
|
|
10862
11732
|
if (t !== undefined && t > 600)
|
|
10863
11733
|
throw new CliError("bad_argument", `--timeout-s must be at most 600, got ${t}.`, "Run login --wait again.");
|
|
10864
|
-
await
|
|
11734
|
+
await run2(async () => args.code ? loginWithCode(ctx, {
|
|
10865
11735
|
code: args.code === "-" ? await readCodeFrom(process.stdin) : args.code,
|
|
10866
11736
|
region: region(args.region),
|
|
10867
11737
|
label: args.label,
|
|
@@ -10885,7 +11755,7 @@ var use = defineCommand({
|
|
|
10885
11755
|
...jsonFlag,
|
|
10886
11756
|
account: { type: "positional", required: true, description: "The account's name or id" }
|
|
10887
11757
|
},
|
|
10888
|
-
run: ({ args }) =>
|
|
11758
|
+
run: ({ args }) => run2(() => useAccount(makeCtx(), { target: args.account }), args)
|
|
10889
11759
|
});
|
|
10890
11760
|
var models = defineCommand({
|
|
10891
11761
|
meta: { name: "models", description: "List models with prices and benchmarks, newest first; or show one" },
|
|
@@ -10905,7 +11775,7 @@ var models = defineCommand({
|
|
|
10905
11775
|
},
|
|
10906
11776
|
search: { type: "string", description: "Match id, name or lab" }
|
|
10907
11777
|
},
|
|
10908
|
-
run: ({ args }) =>
|
|
11778
|
+
run: ({ args }) => run2(() => {
|
|
10909
11779
|
const r = region(args.region);
|
|
10910
11780
|
return args.id ? showModel(makeCtx(), args.id, r) : listModels(makeCtx(), {
|
|
10911
11781
|
live: args.live,
|
|
@@ -10923,7 +11793,7 @@ var account2 = defineCommand({
|
|
|
10923
11793
|
run: ({ args, rawArgs }) => {
|
|
10924
11794
|
if (rawArgs.some((a) => ACCOUNT_SUBCOMMANDS.has(a)))
|
|
10925
11795
|
return;
|
|
10926
|
-
return
|
|
11796
|
+
return run2(() => account(makeCtx()), args);
|
|
10927
11797
|
},
|
|
10928
11798
|
subCommands: {
|
|
10929
11799
|
delete: defineCommand({
|
|
@@ -10935,7 +11805,7 @@ var account2 = defineCommand({
|
|
|
10935
11805
|
...jsonFlag,
|
|
10936
11806
|
confirm: { type: "string", required: true, description: "The account id (acct_...), to confirm" }
|
|
10937
11807
|
},
|
|
10938
|
-
run: ({ args }) =>
|
|
11808
|
+
run: ({ args }) => run2(() => accountDelete(makeCtx(), { confirm: args.confirm }), args)
|
|
10939
11809
|
}),
|
|
10940
11810
|
"ad-measurement": defineCommand({
|
|
10941
11811
|
meta: {
|
|
@@ -10946,7 +11816,7 @@ var account2 = defineCommand({
|
|
|
10946
11816
|
...jsonFlag,
|
|
10947
11817
|
state: { type: "positional", required: true, description: "off or on" }
|
|
10948
11818
|
},
|
|
10949
|
-
run: ({ args }) =>
|
|
11819
|
+
run: ({ args }) => run2(() => accountAdMeasurement(makeCtx(), args.state), args)
|
|
10950
11820
|
})
|
|
10951
11821
|
}
|
|
10952
11822
|
});
|
|
@@ -10956,7 +11826,7 @@ var keys = defineCommand({
|
|
|
10956
11826
|
list: defineCommand({
|
|
10957
11827
|
meta: { name: "list", description: "Keys on this account (prefixes only)" },
|
|
10958
11828
|
args: jsonFlag,
|
|
10959
|
-
run: ({ args }) =>
|
|
11829
|
+
run: ({ args }) => run2(() => keysList(makeCtx()), args)
|
|
10960
11830
|
}),
|
|
10961
11831
|
create: defineCommand({
|
|
10962
11832
|
meta: { name: "create", description: "Create a key; the full key is printed once" },
|
|
@@ -10974,7 +11844,7 @@ var keys = defineCommand({
|
|
|
10974
11844
|
description: "Sub-agent key: budget, scopes and expiry within your key's; revoked when your key is"
|
|
10975
11845
|
}
|
|
10976
11846
|
},
|
|
10977
|
-
run: ({ args }) =>
|
|
11847
|
+
run: ({ args }) => run2(() => keysCreate(makeCtx(), {
|
|
10978
11848
|
name: args.name,
|
|
10979
11849
|
budgetUsd: num("budget-usd", args["budget-usd"]),
|
|
10980
11850
|
expiresIn: args["expires-in"],
|
|
@@ -10982,23 +11852,89 @@ var keys = defineCommand({
|
|
|
10982
11852
|
child: args.child
|
|
10983
11853
|
}), args)
|
|
10984
11854
|
}),
|
|
11855
|
+
put: defineCommand({
|
|
11856
|
+
meta: {
|
|
11857
|
+
name: "put",
|
|
11858
|
+
description: "Put a new child key straight into a platform's secret store as ST_API_KEY, through its own CLI on stdin (never printed). Records where it lives"
|
|
11859
|
+
},
|
|
11860
|
+
args: {
|
|
11861
|
+
...jsonFlag,
|
|
11862
|
+
destination: {
|
|
11863
|
+
type: "positional",
|
|
11864
|
+
required: true,
|
|
11865
|
+
description: `Where: ${Object.keys(DESTINATIONS).join(", ")}`
|
|
11866
|
+
},
|
|
11867
|
+
name: { type: "string", description: "Key name (default: named after the destination)" },
|
|
11868
|
+
"budget-usd": { type: "string", description: "Hard daily budget in USD (default: none beyond your key's)" },
|
|
11869
|
+
"expires-in": { type: "string", description: "Key lifetime, e.g. 30d (max 365d)" },
|
|
11870
|
+
scopes: {
|
|
11871
|
+
type: "string",
|
|
11872
|
+
description: "Comma-separated scopes, a subset of your key's. Default inference,read"
|
|
11873
|
+
},
|
|
11874
|
+
repo: { type: "string", description: "github: owner/name (default: this directory's repo)" },
|
|
11875
|
+
app: { type: "string", description: "github: actions|codespaces|dependabot (default actions); fly: app name" },
|
|
11876
|
+
env: {
|
|
11877
|
+
type: "string",
|
|
11878
|
+
description: "github: deployment environment; vercel: production|preview|development (default production); cloudflare: Wrangler env"
|
|
11879
|
+
},
|
|
11880
|
+
project: { type: "string", description: "vercel (must match the linked project), gcp, doppler: project" },
|
|
11881
|
+
worker: { type: "string", description: "cloudflare: Worker name (default: wrangler.toml's name)" },
|
|
11882
|
+
secret: {
|
|
11883
|
+
type: "string",
|
|
11884
|
+
description: "gcp (default st-api-key), aws (default scaledthought/api-key), k8s (default scaledthought): secret name"
|
|
11885
|
+
},
|
|
11886
|
+
"aws-region": { type: "string", description: "aws: region (default: AWS_REGION or the CLI's)" },
|
|
11887
|
+
locations: {
|
|
11888
|
+
type: "string",
|
|
11889
|
+
description: "gcp: user-managed replication locations, e.g. northamerica-northeast1"
|
|
11890
|
+
},
|
|
11891
|
+
namespace: { type: "string", description: "k8s: namespace (default: the context's)" },
|
|
11892
|
+
context: { type: "string", description: "k8s: kubectl context (default: current)" },
|
|
11893
|
+
config: { type: "string", description: "doppler: config, e.g. prd (default: this directory's)" }
|
|
11894
|
+
},
|
|
11895
|
+
run: ({ args }) => run2(() => keysPut(makeCtx(), args.destination, {
|
|
11896
|
+
name: args.name,
|
|
11897
|
+
budgetUsd: num("budget-usd", args["budget-usd"]),
|
|
11898
|
+
expiresIn: args["expires-in"],
|
|
11899
|
+
scopes: args.scopes?.split(",").map((x) => x.trim()).filter(Boolean),
|
|
11900
|
+
flags: putFlagsFrom(args)
|
|
11901
|
+
}), args)
|
|
11902
|
+
}),
|
|
10985
11903
|
rotate: defineCommand({
|
|
10986
11904
|
meta: {
|
|
10987
11905
|
name: "rotate",
|
|
10988
|
-
description: "Replace a key: prints the new key once; the old key keeps working for --grace (default 24h, 0 to 7d)"
|
|
11906
|
+
description: "Replace a key: prints the new key once; the old key keeps working for --grace (default 24h, 0 to 7d). With --push, puts the new key where the old one lives instead of printing it"
|
|
10989
11907
|
},
|
|
10990
11908
|
args: {
|
|
10991
11909
|
...jsonFlag,
|
|
10992
|
-
id: { type: "positional", required:
|
|
11910
|
+
id: { type: "positional", required: false, description: "Key id (key_…); or --all with --push" },
|
|
10993
11911
|
grace: { type: "string", description: "How long the old key keeps working: 0 to 7d (default 24h)" },
|
|
10994
|
-
"expires-in": { type: "string", description: "New key's lifetime (default: the old key's)" }
|
|
11912
|
+
"expires-in": { type: "string", description: "New key's lifetime (default: the old key's)" },
|
|
11913
|
+
push: {
|
|
11914
|
+
type: "boolean",
|
|
11915
|
+
description: "Put each new key where the old one lives (its lives_in, from keys put); prints no keys"
|
|
11916
|
+
},
|
|
11917
|
+
all: { type: "boolean", description: "With --push: every active key that has a lives_in keys put can reach" }
|
|
10995
11918
|
},
|
|
10996
|
-
run: ({ args }) =>
|
|
11919
|
+
run: ({ args }) => run2(() => {
|
|
11920
|
+
if (args.all && !args.push)
|
|
11921
|
+
throw new CliError("usage_error", "--all needs --push (rotating every key never prints them).");
|
|
11922
|
+
if (args.all && args.id)
|
|
11923
|
+
throw new CliError("usage_error", "Pass a key id or --all, not both.");
|
|
11924
|
+
if (!args.all && !args.id)
|
|
11925
|
+
throw new CliError("usage_error", "Pass a key id (or --all --push).");
|
|
11926
|
+
if (args.push) {
|
|
11927
|
+
if (args["expires-in"])
|
|
11928
|
+
throw new CliError("usage_error", "--expires-in can't be combined with --push.");
|
|
11929
|
+
return keysRotatePush(makeCtx(), { id: args.id, all: args.all, grace: args.grace });
|
|
11930
|
+
}
|
|
11931
|
+
return keysRotate(makeCtx(), args.id, { grace: args.grace, expiresIn: args["expires-in"] });
|
|
11932
|
+
}, args)
|
|
10997
11933
|
}),
|
|
10998
11934
|
revoke: defineCommand({
|
|
10999
11935
|
meta: { name: "revoke", description: "Revoke a key by id (and its sub-agent keys)" },
|
|
11000
11936
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Key id (key_…)" } },
|
|
11001
|
-
run: ({ args }) =>
|
|
11937
|
+
run: ({ args }) => run2(() => keysRevoke(makeCtx(), args.id), args)
|
|
11002
11938
|
}),
|
|
11003
11939
|
"revoke-self": defineCommand({
|
|
11004
11940
|
meta: {
|
|
@@ -11006,7 +11942,7 @@ var keys = defineCommand({
|
|
|
11006
11942
|
description: "Revoke the key you are using now (e.g. if it may have leaked). Irreversible"
|
|
11007
11943
|
},
|
|
11008
11944
|
args: jsonFlag,
|
|
11009
|
-
run: ({ args }) =>
|
|
11945
|
+
run: ({ args }) => run2(() => keysRevokeSelf(makeCtx()), args)
|
|
11010
11946
|
})
|
|
11011
11947
|
}
|
|
11012
11948
|
});
|
|
@@ -11017,7 +11953,7 @@ var usage3 = defineCommand({
|
|
|
11017
11953
|
days: { type: "string", default: "7", description: "Look-back window, 1-90" },
|
|
11018
11954
|
by: { type: "string", default: "model", description: "model|day|key|modality" }
|
|
11019
11955
|
},
|
|
11020
|
-
run: ({ args }) =>
|
|
11956
|
+
run: ({ args }) => run2(() => usage(makeCtx(), { days: num("days", args.days) ?? 7, by: args.by }), args)
|
|
11021
11957
|
});
|
|
11022
11958
|
var chat2 = defineCommand({
|
|
11023
11959
|
meta: { name: "chat", description: "Send one message. Use -m auto to let the router pick" },
|
|
@@ -11028,7 +11964,7 @@ var chat2 = defineCommand({
|
|
|
11028
11964
|
},
|
|
11029
11965
|
run: async ({ args }) => {
|
|
11030
11966
|
const human = !wantsJson(args);
|
|
11031
|
-
await
|
|
11967
|
+
await run2(async () => {
|
|
11032
11968
|
const r = await chat(makeCtx(), {
|
|
11033
11969
|
model: args.model,
|
|
11034
11970
|
prompt: args.prompt,
|
|
@@ -11091,7 +12027,7 @@ var speak2 = defineCommand({
|
|
|
11091
12027
|
speed: { type: "string", description: "0.25 to 4 (default 1)" },
|
|
11092
12028
|
instructions: { type: "string", description: "Delivery instructions for models that take them, optional" }
|
|
11093
12029
|
},
|
|
11094
|
-
run: ({ args }) =>
|
|
12030
|
+
run: ({ args }) => run2(() => speak(makeCtx(), {
|
|
11095
12031
|
text: args.text,
|
|
11096
12032
|
output: args.output,
|
|
11097
12033
|
model: args.model,
|
|
@@ -11114,7 +12050,7 @@ var decideCmd = defineCommand({
|
|
|
11114
12050
|
question: { type: "string", alias: "q", description: "id:type:instructions[:criteria], repeatable" },
|
|
11115
12051
|
file: { type: "string", description: "A JSON request: {state, questions, model?}" }
|
|
11116
12052
|
},
|
|
11117
|
-
run: ({ args }) =>
|
|
12053
|
+
run: ({ args }) => run2(() => {
|
|
11118
12054
|
const ctx = makeCtx();
|
|
11119
12055
|
const raw = currentArgv;
|
|
11120
12056
|
const questions = [];
|
|
@@ -11147,7 +12083,7 @@ var realtime2 = defineCommand({
|
|
|
11147
12083
|
instructions: { type: "string", description: "System instructions for the session (optional)" },
|
|
11148
12084
|
timeout: { type: "string", description: "Seconds to wait for the reply (default 120)" }
|
|
11149
12085
|
},
|
|
11150
|
-
run: ({ args }) =>
|
|
12086
|
+
run: ({ args }) => run2(async () => {
|
|
11151
12087
|
const t = num("timeout", args.timeout);
|
|
11152
12088
|
return realtime(makeCtx(), {
|
|
11153
12089
|
model: args.model,
|
|
@@ -11165,7 +12101,7 @@ function autoReloadArgs(args) {
|
|
|
11165
12101
|
if (below === undefined && amount === undefined && max === undefined)
|
|
11166
12102
|
return;
|
|
11167
12103
|
if (below === undefined || amount === undefined)
|
|
11168
|
-
throw new CliError("bad_argument", "Auto-reload needs both --auto-reload-below and --auto-reload-amount.", "e.g. --auto-reload-below 5 --auto-reload-amount
|
|
12104
|
+
throw new CliError("bad_argument", "Auto-reload needs both --auto-reload-below and --auto-reload-amount.", "e.g. --auto-reload-below 5 --auto-reload-amount 20");
|
|
11169
12105
|
return { threshold_usd: below, amount_usd: amount, ...max !== undefined ? { monthly_limit_usd: max } : {} };
|
|
11170
12106
|
}
|
|
11171
12107
|
var billing = defineCommand({
|
|
@@ -11177,7 +12113,7 @@ var billing = defineCommand({
|
|
|
11177
12113
|
description: "Credit balance, auto-reload, pending top-ups (with their approval links), recent transactions"
|
|
11178
12114
|
},
|
|
11179
12115
|
args: jsonFlag,
|
|
11180
|
-
run: ({ args }) =>
|
|
12116
|
+
run: ({ args }) => run2(() => billingStatus(makeCtx()), args)
|
|
11181
12117
|
}),
|
|
11182
12118
|
topup: defineCommand({
|
|
11183
12119
|
meta: {
|
|
@@ -11214,7 +12150,7 @@ var billing = defineCommand({
|
|
|
11214
12150
|
const autoReload = autoReloadArgs(args);
|
|
11215
12151
|
checkTopupLimits(amountUsd, autoReload);
|
|
11216
12152
|
const noOpen = args.open === false;
|
|
11217
|
-
await
|
|
12153
|
+
await run2(() => args.wait ? billingTopupAndWait(makeCtx(), { amountUsd, autoReload, noOpen, onPending: (t) => out(t, args) }) : billingTopup(makeCtx(), { amountUsd, autoReload, noOpen }), args);
|
|
11218
12154
|
}
|
|
11219
12155
|
}),
|
|
11220
12156
|
wait: defineCommand({
|
|
@@ -11234,7 +12170,7 @@ var billing = defineCommand({
|
|
|
11234
12170
|
const t = args["timeout-s"] === undefined ? undefined : num("timeout-s", args["timeout-s"]);
|
|
11235
12171
|
if (t !== undefined && t > 600)
|
|
11236
12172
|
throw new CliError("bad_argument", `--timeout-s must be at most 600, got ${t}.`, "Run wait again instead.");
|
|
11237
|
-
await
|
|
12173
|
+
await run2(() => billingWait(makeCtx(), { topupId: args.topup_id || undefined, timeoutS: t }), args);
|
|
11238
12174
|
}
|
|
11239
12175
|
}),
|
|
11240
12176
|
"auto-reload": defineCommand({
|
|
@@ -11261,7 +12197,7 @@ var billing = defineCommand({
|
|
|
11261
12197
|
description: "Open the page in the browser (--no-open only prints approval_url)"
|
|
11262
12198
|
}
|
|
11263
12199
|
},
|
|
11264
|
-
run: ({ args }) =>
|
|
12200
|
+
run: ({ args }) => run2(() => billingAutoReloadSetup(makeCtx(), {
|
|
11265
12201
|
thresholdUsd: num("below", args.below),
|
|
11266
12202
|
amountUsd: num("amount", args.amount),
|
|
11267
12203
|
noOpen: args.open === false
|
|
@@ -11270,14 +12206,14 @@ var billing = defineCommand({
|
|
|
11270
12206
|
off: defineCommand({
|
|
11271
12207
|
meta: { name: "off", description: "Disable auto-reload. No approval needed" },
|
|
11272
12208
|
args: jsonFlag,
|
|
11273
|
-
run: ({ args }) =>
|
|
12209
|
+
run: ({ args }) => run2(() => billingAutoReloadOff(makeCtx()), args)
|
|
11274
12210
|
})
|
|
11275
12211
|
}
|
|
11276
12212
|
}),
|
|
11277
12213
|
transactions: defineCommand({
|
|
11278
12214
|
meta: { name: "transactions", description: "Credit ledger: purchases, auto-reloads, usage, batches, refunds" },
|
|
11279
12215
|
args: { ...jsonFlag, cursor: { type: "string", description: "next_cursor from the previous page" } },
|
|
11280
|
-
run: ({ args }) =>
|
|
12216
|
+
run: ({ args }) => run2(() => billingTransactions(makeCtx(), { cursor: args.cursor }), args)
|
|
11281
12217
|
}),
|
|
11282
12218
|
upgrade: defineCommand({
|
|
11283
12219
|
meta: { name: "upgrade", description: "Removed: monthly caps became prepaid credits. Use billing topup" },
|
|
@@ -11296,34 +12232,34 @@ var batches = defineCommand({
|
|
|
11296
12232
|
file: { type: "positional", required: true, description: "Path to requests.jsonl" },
|
|
11297
12233
|
endpoint: { type: "string", default: "/v1/chat/completions", description: "Endpoint every line calls" }
|
|
11298
12234
|
},
|
|
11299
|
-
run: ({ args }) =>
|
|
12235
|
+
run: ({ args }) => run2(() => batchCreate(makeCtx(), { path: args.file, endpoint: args.endpoint }), args)
|
|
11300
12236
|
}),
|
|
11301
12237
|
list: defineCommand({
|
|
11302
12238
|
meta: { name: "list", description: "Your batches" },
|
|
11303
12239
|
args: jsonFlag,
|
|
11304
|
-
run: ({ args }) =>
|
|
12240
|
+
run: ({ args }) => run2(() => batchList(makeCtx()), args)
|
|
11305
12241
|
}),
|
|
11306
12242
|
get: defineCommand({
|
|
11307
12243
|
meta: { name: "get", description: "One batch" },
|
|
11308
12244
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Batch id" } },
|
|
11309
|
-
run: ({ args }) =>
|
|
12245
|
+
run: ({ args }) => run2(() => batchGet(makeCtx(), args.id), args)
|
|
11310
12246
|
}),
|
|
11311
12247
|
results: defineCommand({
|
|
11312
12248
|
meta: { name: "results", description: "Print the output lines of a finished batch" },
|
|
11313
12249
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Batch id" } },
|
|
11314
|
-
run: ({ args }) =>
|
|
12250
|
+
run: ({ args }) => run2(() => batchResults(makeCtx(), args.id), args)
|
|
11315
12251
|
}),
|
|
11316
12252
|
cancel: defineCommand({
|
|
11317
12253
|
meta: { name: "cancel", description: "Cancel a batch" },
|
|
11318
12254
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Batch id" } },
|
|
11319
|
-
run: ({ args }) =>
|
|
12255
|
+
run: ({ args }) => run2(() => batchCancel(makeCtx(), args.id), args)
|
|
11320
12256
|
})
|
|
11321
12257
|
}
|
|
11322
12258
|
});
|
|
11323
12259
|
var benchmarks2 = defineCommand({
|
|
11324
12260
|
meta: { name: "benchmarks", description: "Our own benchmark results for a model" },
|
|
11325
12261
|
args: { ...jsonFlag, ...regionFlag, id: { type: "positional", required: true, description: "Model id" } },
|
|
11326
|
-
run: ({ args }) =>
|
|
12262
|
+
run: ({ args }) => run2(() => benchmarks(makeCtx(), args.id, region(args.region)), args)
|
|
11327
12263
|
});
|
|
11328
12264
|
var fineTune = defineCommand({
|
|
11329
12265
|
meta: {
|
|
@@ -11340,7 +12276,7 @@ var fineTune = defineCommand({
|
|
|
11340
12276
|
suffix: { type: "string", description: "Suffix for the fine-tuned model id" },
|
|
11341
12277
|
epochs: { type: "string", description: "1-10, default 3" }
|
|
11342
12278
|
},
|
|
11343
|
-
run: ({ args }) =>
|
|
12279
|
+
run: ({ args }) => run2(() => fineTuneCreate(makeCtx(), {
|
|
11344
12280
|
path: args.file,
|
|
11345
12281
|
model: args.model,
|
|
11346
12282
|
suffix: args.suffix,
|
|
@@ -11350,27 +12286,27 @@ var fineTune = defineCommand({
|
|
|
11350
12286
|
list: defineCommand({
|
|
11351
12287
|
meta: { name: "list", description: "Your jobs" },
|
|
11352
12288
|
args: jsonFlag,
|
|
11353
|
-
run: ({ args }) =>
|
|
12289
|
+
run: ({ args }) => run2(() => fineTuneList(makeCtx()), args)
|
|
11354
12290
|
}),
|
|
11355
12291
|
get: defineCommand({
|
|
11356
12292
|
meta: { name: "get", description: "One job" },
|
|
11357
12293
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Job id" } },
|
|
11358
|
-
run: ({ args }) =>
|
|
12294
|
+
run: ({ args }) => run2(() => fineTuneGet(makeCtx(), args.id), args)
|
|
11359
12295
|
}),
|
|
11360
12296
|
events: defineCommand({
|
|
11361
12297
|
meta: { name: "events", description: "A job's progress events" },
|
|
11362
12298
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Job id" } },
|
|
11363
|
-
run: ({ args }) =>
|
|
12299
|
+
run: ({ args }) => run2(() => fineTuneEvents(makeCtx(), args.id), args)
|
|
11364
12300
|
}),
|
|
11365
12301
|
cancel: defineCommand({
|
|
11366
12302
|
meta: { name: "cancel", description: "Cancel a job" },
|
|
11367
12303
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Job id" } },
|
|
11368
|
-
run: ({ args }) =>
|
|
12304
|
+
run: ({ args }) => run2(() => fineTuneCancel(makeCtx(), args.id), args)
|
|
11369
12305
|
}),
|
|
11370
12306
|
models: defineCommand({
|
|
11371
12307
|
meta: { name: "models", description: "Your fine-tuned models" },
|
|
11372
12308
|
args: jsonFlag,
|
|
11373
|
-
run: ({ args }) =>
|
|
12309
|
+
run: ({ args }) => run2(() => fineTuneModels(makeCtx()), args)
|
|
11374
12310
|
})
|
|
11375
12311
|
}
|
|
11376
12312
|
});
|
|
@@ -11396,7 +12332,7 @@ var video = defineCommand({
|
|
|
11396
12332
|
wait: { type: "boolean", description: "Wait until the video is ready" },
|
|
11397
12333
|
output: { type: "string", alias: "o", description: "Save the MP4 here (waits for it)" }
|
|
11398
12334
|
},
|
|
11399
|
-
run: ({ args }) =>
|
|
12335
|
+
run: ({ args }) => run2(() => videoCreate(makeCtx(), {
|
|
11400
12336
|
prompt: args.prompt,
|
|
11401
12337
|
model: args.model,
|
|
11402
12338
|
seconds: num("seconds", args.seconds),
|
|
@@ -11410,7 +12346,7 @@ var video = defineCommand({
|
|
|
11410
12346
|
get: defineCommand({
|
|
11411
12347
|
meta: { name: "get", description: "One video job: status, progress, cost" },
|
|
11412
12348
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Video id" } },
|
|
11413
|
-
run: ({ args }) =>
|
|
12349
|
+
run: ({ args }) => run2(() => videoGet(makeCtx(), args.id), args)
|
|
11414
12350
|
}),
|
|
11415
12351
|
list: defineCommand({
|
|
11416
12352
|
meta: { name: "list", description: "Your video jobs, newest first" },
|
|
@@ -11419,7 +12355,7 @@ var video = defineCommand({
|
|
|
11419
12355
|
limit: { type: "string", description: "Page size, 1-100 (default 20)" },
|
|
11420
12356
|
after: { type: "string", description: "Continue after this video id" }
|
|
11421
12357
|
},
|
|
11422
|
-
run: ({ args }) =>
|
|
12358
|
+
run: ({ args }) => run2(() => videoList(makeCtx(), { limit: num("limit", args.limit), after: args.after }), args)
|
|
11423
12359
|
}),
|
|
11424
12360
|
download: defineCommand({
|
|
11425
12361
|
meta: { name: "download", description: "Save a finished video (or --thumbnail) to a file" },
|
|
@@ -11429,12 +12365,12 @@ var video = defineCommand({
|
|
|
11429
12365
|
output: { type: "string", alias: "o", required: true, description: "File to write" },
|
|
11430
12366
|
thumbnail: { type: "boolean", description: "Download the JPEG thumbnail instead" }
|
|
11431
12367
|
},
|
|
11432
|
-
run: ({ args }) =>
|
|
12368
|
+
run: ({ args }) => run2(() => videoDownload(makeCtx(), args.id, args.output, args.thumbnail ? "thumbnail" : "video"), args)
|
|
11433
12369
|
}),
|
|
11434
12370
|
delete: defineCommand({
|
|
11435
12371
|
meta: { name: "delete", description: "Cancel (credits returned) or delete a video and its files" },
|
|
11436
12372
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Video id" } },
|
|
11437
|
-
run: ({ args }) =>
|
|
12373
|
+
run: ({ args }) => run2(() => videoDelete(makeCtx(), args.id), args)
|
|
11438
12374
|
})
|
|
11439
12375
|
}
|
|
11440
12376
|
});
|
|
@@ -11444,7 +12380,7 @@ var status2 = defineCommand({
|
|
|
11444
12380
|
description: "Per-model health in the region (last hour), plus your account's notices when a key is set"
|
|
11445
12381
|
},
|
|
11446
12382
|
args: { ...jsonFlag, ...regionFlag },
|
|
11447
|
-
run: ({ args }) =>
|
|
12383
|
+
run: ({ args }) => run2(() => status(makeCtx(), region(args.region)), args)
|
|
11448
12384
|
});
|
|
11449
12385
|
var doctor2 = defineCommand({
|
|
11450
12386
|
meta: {
|
|
@@ -11452,7 +12388,7 @@ var doctor2 = defineCommand({
|
|
|
11452
12388
|
description: "Check this project's setup: key and region, account, credit, and notices to tell your human"
|
|
11453
12389
|
},
|
|
11454
12390
|
args: jsonFlag,
|
|
11455
|
-
run: ({ args }) =>
|
|
12391
|
+
run: ({ args }) => run2(() => doctor(makeCtx()), args)
|
|
11456
12392
|
});
|
|
11457
12393
|
var webhooks = defineCommand({
|
|
11458
12394
|
meta: {
|
|
@@ -11470,27 +12406,43 @@ var webhooks = defineCommand({
|
|
|
11470
12406
|
description: "Comma-separated events (default: all four)"
|
|
11471
12407
|
}
|
|
11472
12408
|
},
|
|
11473
|
-
run: ({ args }) =>
|
|
12409
|
+
run: ({ args }) => run2(() => webhooksAdd(makeCtx(), { url: args.url, events: args.events }), args)
|
|
11474
12410
|
}),
|
|
11475
12411
|
list: defineCommand({
|
|
11476
12412
|
meta: { name: "list", description: "This account's webhook endpoints" },
|
|
11477
12413
|
args: jsonFlag,
|
|
11478
|
-
run: ({ args }) =>
|
|
12414
|
+
run: ({ args }) => run2(() => webhooksList(makeCtx()), args)
|
|
11479
12415
|
}),
|
|
11480
12416
|
remove: defineCommand({
|
|
11481
12417
|
meta: { name: "remove", description: "Delete an endpoint by id (whk_…)" },
|
|
11482
12418
|
args: { ...jsonFlag, id: { type: "positional", required: true, description: "Webhook id" } },
|
|
11483
|
-
run: ({ args }) =>
|
|
12419
|
+
run: ({ args }) => run2(() => webhooksRemove(makeCtx(), args.id), args)
|
|
11484
12420
|
})
|
|
11485
12421
|
}
|
|
11486
12422
|
});
|
|
12423
|
+
var connectTool = defineCommand({
|
|
12424
|
+
meta: {
|
|
12425
|
+
name: "connect",
|
|
12426
|
+
description: "Point an OpenAI-compatible tool at ScaledThought: writes its config with the base URL and a new child key (never printed), or prints the steps when the tool's key store can't be written safely"
|
|
12427
|
+
},
|
|
12428
|
+
args: {
|
|
12429
|
+
...jsonFlag,
|
|
12430
|
+
tool: { type: "positional", required: true, description: CONNECT_TOOLS.join(", ") },
|
|
12431
|
+
model: { type: "string", description: "Default model (default: the newest live chat model)" },
|
|
12432
|
+
"budget-usd": { type: "string", description: "Hard daily budget for the tool's key, in USD" }
|
|
12433
|
+
},
|
|
12434
|
+
run: ({ args }) => run2(() => connect(makeCtx(), args.tool, {
|
|
12435
|
+
model: args.model,
|
|
12436
|
+
budgetUsd: num("budget-usd", args["budget-usd"])
|
|
12437
|
+
}), args)
|
|
12438
|
+
});
|
|
11487
12439
|
var mcp = defineCommand({
|
|
11488
12440
|
meta: {
|
|
11489
12441
|
name: "mcp",
|
|
11490
12442
|
description: "Print the `claude mcp add` command and JSON config for this account's MCP server"
|
|
11491
12443
|
},
|
|
11492
12444
|
args: jsonFlag,
|
|
11493
|
-
run: ({ args }) =>
|
|
12445
|
+
run: ({ args }) => run2(() => mcpSetup(makeCtx()), args)
|
|
11494
12446
|
});
|
|
11495
12447
|
var dashboard = defineCommand({
|
|
11496
12448
|
meta: {
|
|
@@ -11506,7 +12458,7 @@ var dashboard = defineCommand({
|
|
|
11506
12458
|
},
|
|
11507
12459
|
open: { type: "boolean", default: true, description: "Open it in the browser (--no-open only prints the URL)" }
|
|
11508
12460
|
},
|
|
11509
|
-
run: ({ args }) =>
|
|
12461
|
+
run: ({ args }) => run2(() => dashboardOpen(makeCtx(), {
|
|
11510
12462
|
page: args.page,
|
|
11511
12463
|
email: args.email,
|
|
11512
12464
|
noOpen: args.open === false
|
|
@@ -11515,7 +12467,7 @@ var dashboard = defineCommand({
|
|
|
11515
12467
|
var members2 = defineCommand({
|
|
11516
12468
|
meta: { name: "members", description: "Who can sign in to this account's dashboard (email, role, status)" },
|
|
11517
12469
|
args: jsonFlag,
|
|
11518
|
-
run: ({ args }) =>
|
|
12470
|
+
run: ({ args }) => run2(() => membersList(makeCtx()), args)
|
|
11519
12471
|
});
|
|
11520
12472
|
var root = defineCommand({
|
|
11521
12473
|
meta: {
|
|
@@ -11544,6 +12496,7 @@ var root = defineCommand({
|
|
|
11544
12496
|
video,
|
|
11545
12497
|
webhooks,
|
|
11546
12498
|
doctor: doctor2,
|
|
12499
|
+
connect: connectTool,
|
|
11547
12500
|
mcp,
|
|
11548
12501
|
dashboard,
|
|
11549
12502
|
members: members2
|