@runtypelabs/cli 2.30.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +648 -174
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -72,12 +72,35 @@ var init_credential_store = __esm({
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Replace the entire backing store in one file write. Conf's runtime has a
|
|
77
|
+
* `store` setter (used for exactly this whole-store replacement), but its
|
|
78
|
+
* published typings declare the property read-only, so the assignment needs
|
|
79
|
+
* a cast. Single-write replacement is load-bearing for claim completion:
|
|
80
|
+
* the server has already revoked the previous key, so a multi-write
|
|
81
|
+
* sequence could lose the only copy of the fresh credential.
|
|
82
|
+
*/
|
|
83
|
+
replaceStore(data) {
|
|
84
|
+
;
|
|
85
|
+
this.config.store = data;
|
|
86
|
+
}
|
|
75
87
|
getMachineKey() {
|
|
76
88
|
const hostname4 = os.hostname();
|
|
77
89
|
const username = os.userInfo().username;
|
|
78
90
|
return crypto3.createHash("sha256").update(`runtype-cli-${hostname4}-${username}`).digest("hex").substring(0, 32);
|
|
79
91
|
}
|
|
80
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Store credentials, optionally together with agent-auth claim state in the
|
|
94
|
+
* same write. Passing `claimState` here (rather than a follow-up
|
|
95
|
+
* `saveClaimState` call) keeps the temporary credential and its claim token
|
|
96
|
+
* atomic — a crash between two writes would otherwise orphan a pre-claim
|
|
97
|
+
* key that `auth status` then misreports as a full account.
|
|
98
|
+
*
|
|
99
|
+
* Storing credentials WITHOUT `claimState` invalidates any in-flight claim
|
|
100
|
+
* ceremony: every login path (api-key, browser, claim completion) must not
|
|
101
|
+
* leave stale claim state behind.
|
|
102
|
+
*/
|
|
103
|
+
async saveCredentials(credentials, claimState) {
|
|
81
104
|
const configData = {
|
|
82
105
|
apiKey: this.encrypt(credentials.apiKey),
|
|
83
106
|
userId: credentials.userId,
|
|
@@ -90,7 +113,21 @@ var init_credential_store = __esm({
|
|
|
90
113
|
if (credentials.apiUrl !== void 0) {
|
|
91
114
|
configData.apiUrl = credentials.apiUrl;
|
|
92
115
|
}
|
|
93
|
-
|
|
116
|
+
if (claimState) {
|
|
117
|
+
Object.assign(configData, this.claimStateToConfig(claimState));
|
|
118
|
+
}
|
|
119
|
+
this.replaceStore(configData);
|
|
120
|
+
}
|
|
121
|
+
claimStateToConfig(state) {
|
|
122
|
+
const data = {
|
|
123
|
+
claimToken: this.encrypt(state.claimToken),
|
|
124
|
+
registrationId: state.registrationId
|
|
125
|
+
};
|
|
126
|
+
if (state.claimEmail !== void 0) data.claimEmail = state.claimEmail;
|
|
127
|
+
if (state.credentialExpires !== void 0) data.credentialExpires = state.credentialExpires;
|
|
128
|
+
if (state.claimTokenExpires !== void 0) data.claimTokenExpires = state.claimTokenExpires;
|
|
129
|
+
if (state.apiUrl !== void 0) data.claimApiUrl = state.apiUrl;
|
|
130
|
+
return data;
|
|
94
131
|
}
|
|
95
132
|
async getApiKey() {
|
|
96
133
|
const encrypted = this.config.get("apiKey");
|
|
@@ -109,6 +146,39 @@ var init_credential_store = __esm({
|
|
|
109
146
|
async clearCredentials() {
|
|
110
147
|
this.config.clear();
|
|
111
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Persist agent-auth claim ceremony state alongside the pre-claim credential
|
|
151
|
+
* so `runtype auth claim` / `runtype auth verify` can run as separate
|
|
152
|
+
* invocations (the agent asks the user for their email and OTP in between).
|
|
153
|
+
* The claim token is encrypted like the API key — it can mint the
|
|
154
|
+
* post-claim credential, so it is a secret.
|
|
155
|
+
*/
|
|
156
|
+
async saveClaimState(state) {
|
|
157
|
+
this.config.set(this.claimStateToConfig(state));
|
|
158
|
+
}
|
|
159
|
+
async getClaimState() {
|
|
160
|
+
const claimToken = this.config.get("claimToken");
|
|
161
|
+
const registrationId = this.config.get("registrationId");
|
|
162
|
+
if (!claimToken || !registrationId) return null;
|
|
163
|
+
return {
|
|
164
|
+
claimToken: this.decrypt(claimToken),
|
|
165
|
+
registrationId,
|
|
166
|
+
claimEmail: this.config.get("claimEmail"),
|
|
167
|
+
credentialExpires: this.config.get("credentialExpires"),
|
|
168
|
+
claimTokenExpires: this.config.get("claimTokenExpires"),
|
|
169
|
+
apiUrl: this.config.get("claimApiUrl")
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async clearClaimState() {
|
|
173
|
+
const store = { ...this.config.store };
|
|
174
|
+
delete store.claimToken;
|
|
175
|
+
delete store.registrationId;
|
|
176
|
+
delete store.claimEmail;
|
|
177
|
+
delete store.credentialExpires;
|
|
178
|
+
delete store.claimTokenExpires;
|
|
179
|
+
delete store.claimApiUrl;
|
|
180
|
+
this.replaceStore(store);
|
|
181
|
+
}
|
|
112
182
|
async hasCredentials() {
|
|
113
183
|
return !!this.config.get("apiKey");
|
|
114
184
|
}
|
|
@@ -159,7 +229,7 @@ function loadEnv(path20 = resolve(process.cwd(), ".env")) {
|
|
|
159
229
|
|
|
160
230
|
// src/commands/auth.ts
|
|
161
231
|
import { Command } from "commander";
|
|
162
|
-
import
|
|
232
|
+
import chalk2 from "chalk";
|
|
163
233
|
import React from "react";
|
|
164
234
|
import { render } from "ink";
|
|
165
235
|
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
@@ -38852,6 +38922,9 @@ var coverageInventoryOutputSchema = external_exports.object({
|
|
|
38852
38922
|
instructions: external_exports.array(coverageInstructionSchema)
|
|
38853
38923
|
});
|
|
38854
38924
|
var userProfileFeaturesSchema = external_exports.object({
|
|
38925
|
+
// Agent Skills is permanently enabled — the API always returns `true`. Kept
|
|
38926
|
+
// on the wire (rather than dropped) for compatibility with deployed SPA
|
|
38927
|
+
// bundles and the published SDK that still read this field.
|
|
38855
38928
|
enableAgentSkills: external_exports.boolean(),
|
|
38856
38929
|
// Gates the WebMCP-powered dashboard assistant (copilot panel + chat
|
|
38857
38930
|
// proxy). Driven by the `enable-dashboard-assistant` boolean flag.
|
|
@@ -38883,6 +38956,13 @@ var userProfileFeaturesSchema = external_exports.object({
|
|
|
38883
38956
|
// until a rule enables it). Execution and tool listings are gated
|
|
38884
38957
|
// server-side by the same flag.
|
|
38885
38958
|
enableMassiveWebRender: external_exports.boolean(),
|
|
38959
|
+
// Gates the agent voice configuration UI in the dashboard (the Voice tab in
|
|
38960
|
+
// the agent editor). Driven by the `enable-voice` boolean flag (fail-closed
|
|
38961
|
+
// in production until a rule enables it; non-prod defaults on, preserving
|
|
38962
|
+
// the staging-first rollout the flag replaced). The voice API routes stay
|
|
38963
|
+
// ungated: they already require per-agent `config.voice.enabled` opt-in,
|
|
38964
|
+
// which production users can only set once this flag exposes the UI.
|
|
38965
|
+
enableVoice: external_exports.boolean(),
|
|
38886
38966
|
// Gates the workspace-model arms of the dashboard signup / switcher UI
|
|
38887
38967
|
// (docs/features/planning/2026-07-07-workspace-model-migration.md). Driven by
|
|
38888
38968
|
// the `enable-workspace-model` boolean flag (fail-closed in production until a
|
|
@@ -38938,10 +39018,16 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
38938
39018
|
"anthropic": "claude-opus-4-7",
|
|
38939
39019
|
"vercel": "anthropic/claude-opus-4.7"
|
|
38940
39020
|
},
|
|
39021
|
+
"claude-opus-4-7-fast": {
|
|
39022
|
+
"vercel": "anthropic/claude-opus-4.7-fast"
|
|
39023
|
+
},
|
|
38941
39024
|
"claude-opus-4-8": {
|
|
38942
39025
|
"anthropic": "claude-opus-4-8",
|
|
38943
39026
|
"vercel": "anthropic/claude-opus-4.8"
|
|
38944
39027
|
},
|
|
39028
|
+
"claude-opus-4-8-fast": {
|
|
39029
|
+
"vercel": "anthropic/claude-opus-4.8-fast"
|
|
39030
|
+
},
|
|
38945
39031
|
"claude-opus-4.1": {
|
|
38946
39032
|
"vercel": "anthropic/claude-opus-4.1"
|
|
38947
39033
|
},
|
|
@@ -38954,9 +39040,15 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
38954
39040
|
"claude-opus-4.7": {
|
|
38955
39041
|
"vercel": "anthropic/claude-opus-4.7"
|
|
38956
39042
|
},
|
|
39043
|
+
"claude-opus-4.7-fast": {
|
|
39044
|
+
"vercel": "anthropic/claude-opus-4.7-fast"
|
|
39045
|
+
},
|
|
38957
39046
|
"claude-opus-4.8": {
|
|
38958
39047
|
"vercel": "anthropic/claude-opus-4.8"
|
|
38959
39048
|
},
|
|
39049
|
+
"claude-opus-4.8-fast": {
|
|
39050
|
+
"vercel": "anthropic/claude-opus-4.8-fast"
|
|
39051
|
+
},
|
|
38960
39052
|
"claude-sonnet-4": {
|
|
38961
39053
|
"vercel": "anthropic/claude-sonnet-4"
|
|
38962
39054
|
},
|
|
@@ -39006,9 +39098,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39006
39098
|
"devstral-2": {
|
|
39007
39099
|
"vercel": "mistral/devstral-2"
|
|
39008
39100
|
},
|
|
39009
|
-
"devstral-small": {
|
|
39010
|
-
"vercel": "mistral/devstral-small"
|
|
39011
|
-
},
|
|
39012
39101
|
"devstral-small-2": {
|
|
39013
39102
|
"vercel": "mistral/devstral-small-2"
|
|
39014
39103
|
},
|
|
@@ -39366,6 +39455,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39366
39455
|
"gpt-oss-20b": {
|
|
39367
39456
|
"togetherai": "togetherai/openai/gpt-oss-20b"
|
|
39368
39457
|
},
|
|
39458
|
+
"gpt-realtime-whisper": {
|
|
39459
|
+
"vercel": "openai/gpt-realtime-whisper"
|
|
39460
|
+
},
|
|
39369
39461
|
"grok-4-20-0309-non-reasoning": {
|
|
39370
39462
|
"xai": "grok-4.20-0309-non-reasoning"
|
|
39371
39463
|
},
|
|
@@ -39438,6 +39530,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39438
39530
|
"grok-voice-think-fast-1.0": {
|
|
39439
39531
|
"vercel": "xai/grok-voice-think-fast-1.0"
|
|
39440
39532
|
},
|
|
39533
|
+
"inkling": {
|
|
39534
|
+
"vercel": "thinkingmachines/inkling"
|
|
39535
|
+
},
|
|
39441
39536
|
"interfaze-beta": {
|
|
39442
39537
|
"vercel": "interfaze/interfaze-beta"
|
|
39443
39538
|
},
|
|
@@ -39495,6 +39590,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39495
39590
|
"kimi-k2.7-code-highspeed": {
|
|
39496
39591
|
"vercel": "moonshotai/kimi-k2.7-code-highspeed"
|
|
39497
39592
|
},
|
|
39593
|
+
"kimi-k3": {
|
|
39594
|
+
"vercel": "moonshotai/kimi-k3"
|
|
39595
|
+
},
|
|
39498
39596
|
"lfm2-24b-a2b": {
|
|
39499
39597
|
"togetherai": "togetherai/LiquidAI/LFM2-24B-A2B"
|
|
39500
39598
|
},
|
|
@@ -39546,12 +39644,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39546
39644
|
"llama3-3-70b": {
|
|
39547
39645
|
"vercel": "meta/llama-3.3-70b"
|
|
39548
39646
|
},
|
|
39549
|
-
"longcat-flash-chat": {
|
|
39550
|
-
"vercel": "meituan/longcat-flash-chat"
|
|
39551
|
-
},
|
|
39552
|
-
"longcat-flash-thinking-2601": {
|
|
39553
|
-
"vercel": "meituan/longcat-flash-thinking-2601"
|
|
39554
|
-
},
|
|
39555
39647
|
"magistral-medium": {
|
|
39556
39648
|
"vercel": "mistral/magistral-medium"
|
|
39557
39649
|
},
|
|
@@ -39573,9 +39665,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39573
39665
|
"mimo-v2-5-pro": {
|
|
39574
39666
|
"vercel": "xiaomi/mimo-v2.5-pro"
|
|
39575
39667
|
},
|
|
39576
|
-
"mimo-v2-pro": {
|
|
39577
|
-
"vercel": "xiaomi/mimo-v2-pro"
|
|
39578
|
-
},
|
|
39579
39668
|
"mimo-v2.5": {
|
|
39580
39669
|
"vercel": "xiaomi/mimo-v2.5"
|
|
39581
39670
|
},
|
|
@@ -39714,9 +39803,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39714
39803
|
"pixtral-12b": {
|
|
39715
39804
|
"vercel": "mistral/pixtral-12b"
|
|
39716
39805
|
},
|
|
39717
|
-
"pixtral-large": {
|
|
39718
|
-
"vercel": "mistral/pixtral-large"
|
|
39719
|
-
},
|
|
39720
39806
|
"qwen-3-14b": {
|
|
39721
39807
|
"vercel": "alibaba/qwen-3-14b"
|
|
39722
39808
|
},
|
|
@@ -39894,18 +39980,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39894
39980
|
"qwen3.7-plus": {
|
|
39895
39981
|
"vercel": "alibaba/qwen3.7-plus"
|
|
39896
39982
|
},
|
|
39897
|
-
"rerank-2-5": {
|
|
39898
|
-
"vercel": "voyage/rerank-2.5"
|
|
39899
|
-
},
|
|
39900
|
-
"rerank-2-5-lite": {
|
|
39901
|
-
"vercel": "voyage/rerank-2.5-lite"
|
|
39902
|
-
},
|
|
39903
|
-
"rerank-2.5": {
|
|
39904
|
-
"vercel": "voyage/rerank-2.5"
|
|
39905
|
-
},
|
|
39906
|
-
"rerank-2.5-lite": {
|
|
39907
|
-
"vercel": "voyage/rerank-2.5-lite"
|
|
39908
|
-
},
|
|
39909
39983
|
"rerank-v3-5": {
|
|
39910
39984
|
"vercel": "cohere/rerank-v3.5"
|
|
39911
39985
|
},
|
|
@@ -39966,9 +40040,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
|
|
|
39966
40040
|
"trinity-large": {
|
|
39967
40041
|
"vercel": "arcee-ai/trinity-large-thinking"
|
|
39968
40042
|
},
|
|
39969
|
-
"trinity-large-preview": {
|
|
39970
|
-
"vercel": "arcee-ai/trinity-large-preview"
|
|
39971
|
-
},
|
|
39972
40043
|
"trinity-large-thinking": {
|
|
39973
40044
|
"vercel": "arcee-ai/trinity-large-thinking"
|
|
39974
40045
|
},
|
|
@@ -43433,12 +43504,10 @@ var platformCatalogSchema = external_exports.object({
|
|
|
43433
43504
|
/**
|
|
43434
43505
|
* Agent Skills — loadable SKILL.md context bundles a deployed agent pulls into
|
|
43435
43506
|
* its working context on demand. Distinct from A2A AgentSkill (agent-card
|
|
43436
|
-
* roles).
|
|
43437
|
-
*
|
|
43507
|
+
* roles). Described here rather than listed as a fixed catalog (skills are
|
|
43508
|
+
* user-authored, not a built-in set).
|
|
43438
43509
|
*/
|
|
43439
43510
|
agentSkills: external_exports.object({
|
|
43440
|
-
/** Rollout flag controlling availability; off in production until ramped. */
|
|
43441
|
-
rolloutFlag: external_exports.string(),
|
|
43442
43511
|
description: external_exports.string(),
|
|
43443
43512
|
/** How an agent acquires a bound skill at runtime. */
|
|
43444
43513
|
loadModel: external_exports.string(),
|
|
@@ -43560,7 +43629,6 @@ var PLATFORM_CATALOG = {
|
|
|
43560
43629
|
}
|
|
43561
43630
|
],
|
|
43562
43631
|
agentSkills: {
|
|
43563
|
-
rolloutFlag: "enable-agent-skills",
|
|
43564
43632
|
description: "Loadable SKILL.md context bundles (Anthropic frontmatter + a runtype: capability block) that a deployed agent pulls into its working context on demand.",
|
|
43565
43633
|
loadModel: "A bound skill is surfaced as a `skill:<slug>` virtual tool \u2014 the model loads it on demand (L1 description in the tool list \u2192 L2 markdown body returned on call); bound capabilities activate for later turns.",
|
|
43566
43634
|
governance: "Two-plane: admin/control plane (REST /v1/skills, scoped by SKILLS:READ/WRITE, no review queue) vs deployed-agent/data plane (propose_skill, review-by-default via /v1/skill-proposals).",
|
|
@@ -44016,7 +44084,7 @@ ${renderDefaultModelStrengthsGuide()}`;
|
|
|
44016
44084
|
- **Built-in tools**: GPT Image 2 image generation, provider-native web search (OpenAI web search, Anthropic web search/fetch, xAI Live Search for Grok-3 series, xAI X Search / Web Search agentic tools for Grok-4 series), Exa (AI search), Firecrawl (simple web scraping), Browser Run tools (rendered markdown, HTML, screenshots, snapshots, selector scraping, JSON extraction, link harvesting \u2014 stateless), Browser Session tools (persistent interactive browsing: open, navigate, click, type, scroll, screenshot, evaluate, close), Browser Agent (autonomous higher-level alternative \u2014 give a natural-language goal plus a startUrl or sessionId and the agent navigates/clicks/extracts on its own), Store Asset (host any file from a URL or base64 inline content \u2014 returns a permanent public or signed private URL, downloads as attachment), Publish Page (host HTML that renders inline in the browser at a shareable 7-day /preview/ URL under a locked-down CSP), vector search, UCP commerce tools (discovery, catalog, cart, checkout, orders for any UCP-enabled merchant)
|
|
44017
44085
|
- **Integrations**: ${integrations}
|
|
44018
44086
|
- **Flow step types**: prompt, ${stepTypes}
|
|
44019
|
-
- **Agent Skills**: loadable SKILL.md context bundles an agent pulls in on demand via a \`skill:<slug>\` tool (L1 description \u2192 L2 body), optionally binding flows/subagents/tools.
|
|
44087
|
+
- **Agent Skills**: loadable SKILL.md context bundles an agent pulls in on demand via a \`skill:<slug>\` tool (L1 description \u2192 L2 body), optionally binding flows/subagents/tools. See the Agent Skills guide.
|
|
44020
44088
|
- **Available models**: ${models}
|
|
44021
44089
|
- **Surface types**: ${PLATFORM_CATALOG.availableSurfaceTypes.join(", ")}`;
|
|
44022
44090
|
})();
|
|
@@ -45929,7 +45997,18 @@ var ApiKeyManager = class {
|
|
|
45929
45997
|
try {
|
|
45930
45998
|
data = await client.get("/auth/me");
|
|
45931
45999
|
} catch {
|
|
45932
|
-
|
|
46000
|
+
try {
|
|
46001
|
+
const profile = await client.get(
|
|
46002
|
+
"/users/profile"
|
|
46003
|
+
);
|
|
46004
|
+
if (typeof profile.id === "string") {
|
|
46005
|
+
data = { userId: profile.id, orgId: profile.orgId ?? null, email: profile.email };
|
|
46006
|
+
}
|
|
46007
|
+
} catch {
|
|
46008
|
+
}
|
|
46009
|
+
if (!data) {
|
|
46010
|
+
throw new Error("Failed to get user information");
|
|
46011
|
+
}
|
|
45933
46012
|
}
|
|
45934
46013
|
if (!isAuthMeResponse(data)) {
|
|
45935
46014
|
throw new Error("Invalid user response format");
|
|
@@ -46176,23 +46255,418 @@ function MutationResult({
|
|
|
46176
46255
|
return null;
|
|
46177
46256
|
}
|
|
46178
46257
|
|
|
46179
|
-
// src/commands/auth.ts
|
|
46180
|
-
|
|
46181
|
-
|
|
46258
|
+
// src/commands/auth-agent.ts
|
|
46259
|
+
init_credential_store();
|
|
46260
|
+
|
|
46261
|
+
// src/auth/agent-auth-client.ts
|
|
46262
|
+
var AgentAuthError = class extends Error {
|
|
46263
|
+
constructor(code, status, message, hint) {
|
|
46264
|
+
super(message);
|
|
46265
|
+
this.code = code;
|
|
46266
|
+
this.status = status;
|
|
46267
|
+
this.hint = hint;
|
|
46268
|
+
this.name = "AgentAuthError";
|
|
46269
|
+
}
|
|
46270
|
+
code;
|
|
46271
|
+
status;
|
|
46272
|
+
hint;
|
|
46273
|
+
};
|
|
46274
|
+
var ERROR_HINTS = {
|
|
46275
|
+
email_already_registered: "This email already has a Runtype account, so it cannot claim a new agent account. Ask the user to sign in at the dashboard, create an API key (Settings > API keys), and give it to you. Then run: runtype auth login --api-key <key> --no-tty",
|
|
46276
|
+
invalid_claim_token: "The claim token is invalid or expired. Run: runtype auth register --force",
|
|
46277
|
+
claim_expired: "The claim window expired. Run: runtype auth register --force",
|
|
46278
|
+
credential_expired: "The temporary credential expired. Run: runtype auth register --force",
|
|
46279
|
+
claimed_or_in_flight: "A claim is already in progress. Ask the user for the 6-digit code from their email, then run: runtype auth verify <code>. If the code expired, re-run: runtype auth claim <email>",
|
|
46280
|
+
otp_invalid: "That code is not correct. Ask the user to re-check the 6-digit code from the most recent Runtype email and run: runtype auth verify <code>",
|
|
46281
|
+
otp_expired: "The code expired. Re-send it with: runtype auth claim <email>",
|
|
46282
|
+
previously_claimed: "This registration was already claimed. Run: runtype auth status to inspect stored credentials.",
|
|
46283
|
+
email_send_failed: "The verification email could not be sent. Retry: runtype auth claim <email>",
|
|
46284
|
+
rate_limited: "Too many attempts. Wait a minute, then retry the same command.",
|
|
46285
|
+
service_disabled: "Agent registration is temporarily disabled. Try again later."
|
|
46286
|
+
};
|
|
46287
|
+
async function postJson(url2, body) {
|
|
46288
|
+
let response;
|
|
46182
46289
|
try {
|
|
46183
|
-
|
|
46184
|
-
|
|
46290
|
+
response = await fetch(url2, {
|
|
46291
|
+
method: "POST",
|
|
46292
|
+
headers: {
|
|
46293
|
+
"content-type": "application/json",
|
|
46294
|
+
"user-agent": "runtype-cli"
|
|
46295
|
+
},
|
|
46296
|
+
body: JSON.stringify(body)
|
|
46297
|
+
});
|
|
46298
|
+
} catch (error51) {
|
|
46299
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
46300
|
+
throw new AgentAuthError(
|
|
46301
|
+
"network_error",
|
|
46302
|
+
0,
|
|
46303
|
+
`Could not reach ${url2}: ${message}`,
|
|
46304
|
+
"Check network access and the --api-url value, then retry the same command."
|
|
46305
|
+
);
|
|
46306
|
+
}
|
|
46307
|
+
let data = {};
|
|
46308
|
+
try {
|
|
46309
|
+
data = await response.json();
|
|
46185
46310
|
} catch {
|
|
46186
|
-
return null;
|
|
46187
46311
|
}
|
|
46312
|
+
if (!response.ok) {
|
|
46313
|
+
const code = typeof data.error === "string" ? data.error : `http_${response.status}`;
|
|
46314
|
+
const message = typeof data.message === "string" ? data.message : `Request failed (${response.status})`;
|
|
46315
|
+
throw new AgentAuthError(code, response.status, message, ERROR_HINTS[code]);
|
|
46316
|
+
}
|
|
46317
|
+
return { status: response.status, data };
|
|
46188
46318
|
}
|
|
46189
|
-
|
|
46190
|
-
|
|
46319
|
+
function requireString(data, key) {
|
|
46320
|
+
const value = data[key];
|
|
46321
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
46322
|
+
throw new AgentAuthError(
|
|
46323
|
+
"invalid_response",
|
|
46324
|
+
0,
|
|
46325
|
+
`Registration response is missing "${key}". The API may be an incompatible version.`
|
|
46326
|
+
);
|
|
46327
|
+
}
|
|
46328
|
+
return value;
|
|
46329
|
+
}
|
|
46330
|
+
function stringArray(value) {
|
|
46331
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
46332
|
+
}
|
|
46333
|
+
async function registerAnonymousAgent(apiUrl) {
|
|
46334
|
+
const base = apiUrl || getApiUrl();
|
|
46335
|
+
const { data } = await postJson(`${base}/agent/auth`, {
|
|
46336
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46337
|
+
type: "anonymous",
|
|
46338
|
+
requested_credential_type: "api_key"
|
|
46339
|
+
// @snake-case-ok-end
|
|
46340
|
+
});
|
|
46341
|
+
return {
|
|
46342
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46343
|
+
registrationId: requireString(data, "registration_id"),
|
|
46344
|
+
credential: requireString(data, "credential"),
|
|
46345
|
+
credentialExpires: requireString(data, "credential_expires"),
|
|
46346
|
+
scopes: stringArray(data.scopes),
|
|
46347
|
+
claimToken: requireString(data, "claim_token"),
|
|
46348
|
+
claimTokenExpires: requireString(data, "claim_token_expires"),
|
|
46349
|
+
postClaimScopes: stringArray(data.post_claim_scopes)
|
|
46350
|
+
// @snake-case-ok-end
|
|
46351
|
+
};
|
|
46352
|
+
}
|
|
46353
|
+
async function initiateClaim(claimToken, email3, apiUrl) {
|
|
46354
|
+
const base = apiUrl || getApiUrl();
|
|
46355
|
+
const { data } = await postJson(`${base}/agent/auth/claim`, {
|
|
46356
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46357
|
+
claim_token: claimToken,
|
|
46358
|
+
email: email3
|
|
46359
|
+
// @snake-case-ok-end
|
|
46360
|
+
});
|
|
46361
|
+
return {
|
|
46362
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46363
|
+
registrationId: requireString(data, "registration_id"),
|
|
46364
|
+
status: typeof data.status === "string" ? data.status : "initiated",
|
|
46365
|
+
otpExpiresAt: requireString(data, "expires_at")
|
|
46366
|
+
// @snake-case-ok-end
|
|
46367
|
+
};
|
|
46368
|
+
}
|
|
46369
|
+
async function completeClaim(claimToken, otp, apiUrl) {
|
|
46370
|
+
const base = apiUrl || getApiUrl();
|
|
46371
|
+
const { data } = await postJson(`${base}/agent/auth/claim/complete`, {
|
|
46372
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46373
|
+
claim_token: claimToken,
|
|
46374
|
+
otp
|
|
46375
|
+
// @snake-case-ok-end
|
|
46376
|
+
});
|
|
46377
|
+
return {
|
|
46378
|
+
// @snake-case-ok-start: auth.md protocol field names
|
|
46379
|
+
registrationId: requireString(data, "registration_id"),
|
|
46380
|
+
credential: requireString(data, "credential"),
|
|
46381
|
+
scopes: stringArray(data.scopes)
|
|
46382
|
+
// @snake-case-ok-end
|
|
46383
|
+
};
|
|
46384
|
+
}
|
|
46385
|
+
|
|
46386
|
+
// src/lib/output.ts
|
|
46387
|
+
import chalk from "chalk";
|
|
46388
|
+
function printJson(data) {
|
|
46389
|
+
console.log(JSON.stringify(data, null, 2));
|
|
46390
|
+
}
|
|
46391
|
+
function printList(items, options) {
|
|
46392
|
+
console.log(chalk.cyan(`${options.title}:`));
|
|
46393
|
+
if (items.length === 0) {
|
|
46394
|
+
console.log(chalk.gray(` ${options.emptyMessage || "None found"}`));
|
|
46395
|
+
return;
|
|
46396
|
+
}
|
|
46397
|
+
for (const item of items) {
|
|
46398
|
+
const parts = [];
|
|
46399
|
+
for (const col of options.columns) {
|
|
46400
|
+
const value = item[col.key];
|
|
46401
|
+
if (value === void 0 || value === null) continue;
|
|
46402
|
+
const str = String(value);
|
|
46403
|
+
const colorFn = col.color ? chalk[col.color] : (s) => s;
|
|
46404
|
+
if (col.label) {
|
|
46405
|
+
parts.push(`${col.label}: ${colorFn(str)}`);
|
|
46406
|
+
} else {
|
|
46407
|
+
parts.push(colorFn(str));
|
|
46408
|
+
}
|
|
46409
|
+
}
|
|
46410
|
+
console.log(` ${parts.join(" - ")}`);
|
|
46411
|
+
}
|
|
46412
|
+
if (options.total !== void 0) {
|
|
46413
|
+
console.log(chalk.dim(`
|
|
46414
|
+
Total: ${options.total}`));
|
|
46415
|
+
}
|
|
46416
|
+
}
|
|
46417
|
+
function printDetail(title, fields) {
|
|
46418
|
+
console.log(chalk.cyan(`${title}:`));
|
|
46419
|
+
for (const field of fields) {
|
|
46420
|
+
if (field.value === void 0 || field.value === null) continue;
|
|
46421
|
+
const str = typeof field.value === "object" ? JSON.stringify(field.value) : String(field.value);
|
|
46422
|
+
console.log(` ${chalk.gray(field.label + ":")} ${str}`);
|
|
46423
|
+
}
|
|
46424
|
+
}
|
|
46425
|
+
function getTotalCount(pagination) {
|
|
46426
|
+
return pagination?.totalCount ?? pagination?.total ?? void 0;
|
|
46427
|
+
}
|
|
46428
|
+
function buildListParams(options, extra) {
|
|
46429
|
+
const params = {};
|
|
46430
|
+
if (options.limit) params.limit = options.limit;
|
|
46431
|
+
if (options.cursor) params.cursor = options.cursor;
|
|
46432
|
+
if (extra) {
|
|
46433
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
46434
|
+
if (value !== void 0) params[key] = value;
|
|
46435
|
+
}
|
|
46436
|
+
}
|
|
46437
|
+
return params;
|
|
46438
|
+
}
|
|
46439
|
+
function printNextCursorHint(commandPath, pagination) {
|
|
46440
|
+
const hasMore = pagination?.hasMore ?? pagination?.hasNextPage ?? false;
|
|
46441
|
+
const nextCursor = pagination?.nextCursor !== void 0 ? pagination.nextCursor : pagination?.cursor ?? null;
|
|
46442
|
+
if (!hasMore || !nextCursor) return;
|
|
46443
|
+
console.log(
|
|
46444
|
+
chalk.yellow("\n More results available \u2014 fetch the next page with:") + chalk.dim(`
|
|
46445
|
+
${commandPath} --cursor ${nextCursor}`)
|
|
46446
|
+
);
|
|
46447
|
+
}
|
|
46448
|
+
|
|
46449
|
+
// src/commands/auth-agent.ts
|
|
46450
|
+
var VERIFY_NEXT = "Ask the user for the 6-digit code from their email, then run: runtype auth verify <code>";
|
|
46451
|
+
function claimNext(email3) {
|
|
46452
|
+
return email3 ? `Send the verification email with: runtype auth claim ${email3}` : "Ask the user for their email address, then run: runtype auth claim <email>";
|
|
46453
|
+
}
|
|
46454
|
+
function fail(payload) {
|
|
46455
|
+
console.error(JSON.stringify(payload, null, 2));
|
|
46456
|
+
process.exit(1);
|
|
46457
|
+
}
|
|
46458
|
+
function failFromError(error51, fallbackNext) {
|
|
46459
|
+
if (error51 instanceof AgentAuthError) {
|
|
46460
|
+
fail({
|
|
46461
|
+
status: "error",
|
|
46462
|
+
code: error51.code,
|
|
46463
|
+
error: error51.message,
|
|
46464
|
+
...error51.hint || fallbackNext ? { next: error51.hint || fallbackNext } : {}
|
|
46465
|
+
});
|
|
46466
|
+
}
|
|
46467
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
46468
|
+
fail({ status: "error", error: message, ...fallbackNext ? { next: fallbackNext } : {} });
|
|
46469
|
+
}
|
|
46470
|
+
async function fetchIdentity(apiKey, apiUrl) {
|
|
46471
|
+
const client = createCliClient(apiKey, apiUrl);
|
|
46472
|
+
const profile = await client.get(
|
|
46473
|
+
"/users/profile"
|
|
46474
|
+
);
|
|
46475
|
+
return {
|
|
46476
|
+
userId: typeof profile.id === "string" ? profile.id : "unknown",
|
|
46477
|
+
orgId: typeof profile.orgId === "string" ? profile.orgId : void 0,
|
|
46478
|
+
email: typeof profile.email === "string" ? profile.email : void 0
|
|
46479
|
+
};
|
|
46480
|
+
}
|
|
46481
|
+
async function registerAction(options) {
|
|
46482
|
+
const store = new CredentialStore();
|
|
46483
|
+
const apiUrl = options.apiUrl || getApiUrl();
|
|
46484
|
+
const existingClaim = await store.getClaimState();
|
|
46485
|
+
const hasCredentials = await store.hasCredentials();
|
|
46486
|
+
if (existingClaim && !options.force) {
|
|
46487
|
+
const resumeDeadline = existingClaim.claimTokenExpires ?? existingClaim.credentialExpires;
|
|
46488
|
+
const expired = resumeDeadline && new Date(resumeDeadline) < /* @__PURE__ */ new Date();
|
|
46489
|
+
if (!expired) {
|
|
46490
|
+
const payload = {
|
|
46491
|
+
status: "registration_pending",
|
|
46492
|
+
registrationId: existingClaim.registrationId,
|
|
46493
|
+
credentialExpires: existingClaim.credentialExpires,
|
|
46494
|
+
next: existingClaim.claimEmail ? VERIFY_NEXT : claimNext(options.email)
|
|
46495
|
+
};
|
|
46496
|
+
if (existingClaim.claimEmail) payload.claimEmail = existingClaim.claimEmail;
|
|
46497
|
+
printJson(payload);
|
|
46498
|
+
return;
|
|
46499
|
+
}
|
|
46500
|
+
}
|
|
46501
|
+
if (hasCredentials && !existingClaim && !options.force) {
|
|
46502
|
+
fail({
|
|
46503
|
+
status: "error",
|
|
46504
|
+
code: "already_authenticated",
|
|
46505
|
+
error: "Credentials are already stored for a claimed account.",
|
|
46506
|
+
next: "Run: runtype auth whoami --no-tty to confirm, or runtype auth logout first if the user wants a different account."
|
|
46507
|
+
});
|
|
46508
|
+
}
|
|
46509
|
+
let registration;
|
|
46510
|
+
try {
|
|
46511
|
+
registration = await registerAnonymousAgent(apiUrl);
|
|
46512
|
+
} catch (error51) {
|
|
46513
|
+
failFromError(error51, "Retry: runtype auth register");
|
|
46514
|
+
}
|
|
46515
|
+
let identity = { userId: "unknown" };
|
|
46516
|
+
try {
|
|
46517
|
+
identity = await fetchIdentity(registration.credential, apiUrl);
|
|
46518
|
+
} catch {
|
|
46519
|
+
}
|
|
46520
|
+
const claimState = {
|
|
46521
|
+
claimToken: registration.claimToken,
|
|
46522
|
+
registrationId: registration.registrationId,
|
|
46523
|
+
credentialExpires: registration.credentialExpires,
|
|
46524
|
+
claimTokenExpires: registration.claimTokenExpires,
|
|
46525
|
+
apiUrl
|
|
46526
|
+
};
|
|
46527
|
+
await store.saveCredentials(
|
|
46528
|
+
{
|
|
46529
|
+
apiKey: registration.credential,
|
|
46530
|
+
userId: identity.userId,
|
|
46531
|
+
orgId: identity.orgId,
|
|
46532
|
+
apiUrl
|
|
46533
|
+
},
|
|
46534
|
+
claimState
|
|
46535
|
+
);
|
|
46536
|
+
const base = {
|
|
46537
|
+
status: "registered",
|
|
46538
|
+
accountState: "temporary",
|
|
46539
|
+
registrationId: registration.registrationId,
|
|
46540
|
+
userId: identity.userId,
|
|
46541
|
+
credentialExpires: registration.credentialExpires,
|
|
46542
|
+
note: "A temporary account is active: CLI commands work now with limited models and quota. Verifying an email upgrades it to a full account and replaces the stored credential automatically."
|
|
46543
|
+
};
|
|
46544
|
+
if (options.email) {
|
|
46545
|
+
try {
|
|
46546
|
+
const claim = await initiateClaim(registration.claimToken, options.email, apiUrl);
|
|
46547
|
+
await store.saveClaimState({ ...claimState, claimEmail: options.email });
|
|
46548
|
+
printJson({
|
|
46549
|
+
...base,
|
|
46550
|
+
status: "otp_sent",
|
|
46551
|
+
claimEmail: options.email,
|
|
46552
|
+
otpExpiresAt: claim.otpExpiresAt,
|
|
46553
|
+
next: VERIFY_NEXT
|
|
46554
|
+
});
|
|
46555
|
+
return;
|
|
46556
|
+
} catch (error51) {
|
|
46557
|
+
if (error51 instanceof AgentAuthError) {
|
|
46558
|
+
fail({
|
|
46559
|
+
...base,
|
|
46560
|
+
status: "registered_claim_failed",
|
|
46561
|
+
code: error51.code,
|
|
46562
|
+
error: error51.message,
|
|
46563
|
+
next: error51.hint || claimNext(options.email)
|
|
46564
|
+
});
|
|
46565
|
+
}
|
|
46566
|
+
throw error51;
|
|
46567
|
+
}
|
|
46568
|
+
}
|
|
46569
|
+
printJson({ ...base, next: claimNext() });
|
|
46570
|
+
}
|
|
46571
|
+
async function claimAction(email3, options) {
|
|
46572
|
+
const store = new CredentialStore();
|
|
46573
|
+
const claimState = await store.getClaimState();
|
|
46574
|
+
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
46575
|
+
if (!claimState) {
|
|
46576
|
+
const hasCredentials = await store.hasCredentials();
|
|
46577
|
+
fail({
|
|
46578
|
+
status: "error",
|
|
46579
|
+
code: "no_pending_registration",
|
|
46580
|
+
error: hasCredentials ? "Stored credentials are already claimed; there is nothing to claim." : "No registration found.",
|
|
46581
|
+
next: hasCredentials ? "Run: runtype auth whoami --no-tty" : `Run: runtype auth register --email ${email3}`
|
|
46582
|
+
});
|
|
46583
|
+
}
|
|
46584
|
+
try {
|
|
46585
|
+
const claim = await initiateClaim(claimState.claimToken, email3, apiUrl);
|
|
46586
|
+
await store.saveClaimState({ ...claimState, claimEmail: email3 });
|
|
46587
|
+
printJson({
|
|
46588
|
+
status: "otp_sent",
|
|
46589
|
+
registrationId: claim.registrationId,
|
|
46590
|
+
claimEmail: email3,
|
|
46591
|
+
otpExpiresAt: claim.otpExpiresAt,
|
|
46592
|
+
next: VERIFY_NEXT
|
|
46593
|
+
});
|
|
46594
|
+
} catch (error51) {
|
|
46595
|
+
failFromError(error51, `Retry: runtype auth claim ${email3}`);
|
|
46596
|
+
}
|
|
46597
|
+
}
|
|
46598
|
+
async function verifyAction(code, options) {
|
|
46599
|
+
const store = new CredentialStore();
|
|
46600
|
+
const claimState = await store.getClaimState();
|
|
46601
|
+
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
46602
|
+
if (!claimState) {
|
|
46603
|
+
fail({
|
|
46604
|
+
status: "error",
|
|
46605
|
+
code: "no_pending_registration",
|
|
46606
|
+
error: "No claim in progress.",
|
|
46607
|
+
next: "Run: runtype auth register --email <email> first."
|
|
46608
|
+
});
|
|
46609
|
+
}
|
|
46610
|
+
if (!claimState.claimEmail) {
|
|
46611
|
+
fail({
|
|
46612
|
+
status: "error",
|
|
46613
|
+
code: "claim_not_initiated",
|
|
46614
|
+
error: "No verification email has been sent yet, so there is no code to verify.",
|
|
46615
|
+
next: claimNext()
|
|
46616
|
+
});
|
|
46617
|
+
}
|
|
46618
|
+
let completed;
|
|
46619
|
+
try {
|
|
46620
|
+
completed = await completeClaim(claimState.claimToken, code.trim(), apiUrl);
|
|
46621
|
+
} catch (error51) {
|
|
46622
|
+
failFromError(error51);
|
|
46623
|
+
}
|
|
46624
|
+
let identity = { userId: "unknown" };
|
|
46625
|
+
try {
|
|
46626
|
+
identity = await fetchIdentity(completed.credential, apiUrl);
|
|
46627
|
+
} catch {
|
|
46628
|
+
}
|
|
46629
|
+
await store.saveCredentials({
|
|
46630
|
+
apiKey: completed.credential,
|
|
46631
|
+
userId: identity.userId,
|
|
46632
|
+
orgId: identity.orgId,
|
|
46633
|
+
apiUrl
|
|
46634
|
+
});
|
|
46635
|
+
await store.clearClaimState();
|
|
46636
|
+
printJson({
|
|
46637
|
+
status: "authenticated",
|
|
46638
|
+
accountState: "full",
|
|
46639
|
+
registrationId: completed.registrationId,
|
|
46640
|
+
userId: identity.userId,
|
|
46641
|
+
email: identity.email ?? claimState.claimEmail,
|
|
46642
|
+
note: "The account is fully activated and the stored credential was rotated. All limits from the temporary account are removed.",
|
|
46643
|
+
next: "Continue with setup: runtype install-mcp (then confirm with: runtype auth whoami --no-tty)"
|
|
46644
|
+
});
|
|
46645
|
+
}
|
|
46646
|
+
function registerAgentAuthCommands(authCommand2) {
|
|
46647
|
+
authCommand2.command("register").description("Create a Runtype account without a browser (agent signup, step 1 of 2)").option("--email <email>", "Also send the verification email in the same step").option("--force", "Register a new account even if one is stored or pending").option("--api-url <url>", "Custom API URL").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(registerAction);
|
|
46648
|
+
authCommand2.command("claim <email>").description("Email the user a 6-digit code to attach the account to them").option("--api-url <url>", "Custom API URL").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(claimAction);
|
|
46649
|
+
authCommand2.command("verify <code>").description("Complete signup with the 6-digit code from the verification email (step 2 of 2)").option("--api-url <url>", "Custom API URL").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(verifyAction);
|
|
46650
|
+
}
|
|
46651
|
+
|
|
46652
|
+
// src/commands/auth.ts
|
|
46653
|
+
var authCommand = new Command("auth").description("Manage authentication");
|
|
46654
|
+
var NON_TTY_LOGIN_GUIDANCE = {
|
|
46655
|
+
status: "error",
|
|
46656
|
+
code: "tty_required",
|
|
46657
|
+
error: "Browser login needs an interactive terminal, which this environment does not have.",
|
|
46658
|
+
next: [
|
|
46659
|
+
"New account (no browser needed): ask the user for their email, then run: runtype auth register --email <email> \u2014 a 6-digit code is emailed to them; finish with: runtype auth verify <code>",
|
|
46660
|
+
"Existing account: ask the user for an API key from the dashboard (Settings > API keys), then run: runtype auth login --api-key <key>"
|
|
46661
|
+
]
|
|
46662
|
+
};
|
|
46663
|
+
function refuseForcedTtyLogin(options) {
|
|
46664
|
+
if (options.tty === true && process.stdout.isTTY !== true) {
|
|
46191
46665
|
console.error(
|
|
46192
46666
|
JSON.stringify(
|
|
46193
46667
|
{
|
|
46194
|
-
|
|
46195
|
-
error: "Browser login
|
|
46668
|
+
...NON_TTY_LOGIN_GUIDANCE,
|
|
46669
|
+
error: "Browser login needs a real interactive terminal. Forcing --tty from a non-terminal only changes output formatting; the browser OAuth callback still cannot complete here, so the command would hang."
|
|
46196
46670
|
},
|
|
46197
46671
|
null,
|
|
46198
46672
|
2
|
|
@@ -46200,6 +46674,22 @@ authCommand.command("signup").description('Create a new Runtype account (alias f
|
|
|
46200
46674
|
);
|
|
46201
46675
|
process.exit(1);
|
|
46202
46676
|
}
|
|
46677
|
+
return false;
|
|
46678
|
+
}
|
|
46679
|
+
async function getBillingInfo(apiKey, apiUrl) {
|
|
46680
|
+
try {
|
|
46681
|
+
const client = createCliClient(apiKey, apiUrl);
|
|
46682
|
+
return await client.get("/billing/status");
|
|
46683
|
+
} catch {
|
|
46684
|
+
return null;
|
|
46685
|
+
}
|
|
46686
|
+
}
|
|
46687
|
+
authCommand.command("signup").description('Create a new Runtype account (alias for "auth login")').option("--api-url <url>", "Custom API URL").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
46688
|
+
refuseForcedTtyLogin(options);
|
|
46689
|
+
if (!isTTY(options)) {
|
|
46690
|
+
console.error(JSON.stringify(NON_TTY_LOGIN_GUIDANCE, null, 2));
|
|
46691
|
+
process.exit(1);
|
|
46692
|
+
}
|
|
46203
46693
|
const App = () => {
|
|
46204
46694
|
const [loading, setLoading] = useState2(true);
|
|
46205
46695
|
const [loadingLabel, setLoadingLabel] = useState2("Opening browser for authentication...");
|
|
@@ -46272,7 +46762,11 @@ async function loginAction(options) {
|
|
|
46272
46762
|
});
|
|
46273
46763
|
console.log(
|
|
46274
46764
|
JSON.stringify(
|
|
46275
|
-
{
|
|
46765
|
+
{
|
|
46766
|
+
status: "authenticated",
|
|
46767
|
+
userId: userData.userId,
|
|
46768
|
+
next: "Continue with setup: runtype install-mcp (then confirm with: runtype auth whoami)"
|
|
46769
|
+
},
|
|
46276
46770
|
null,
|
|
46277
46771
|
2
|
|
46278
46772
|
)
|
|
@@ -46330,14 +46824,9 @@ async function loginAction(options) {
|
|
|
46330
46824
|
await waitUntilExit2();
|
|
46331
46825
|
return;
|
|
46332
46826
|
}
|
|
46827
|
+
refuseForcedTtyLogin(options);
|
|
46333
46828
|
if (!isTTY(options)) {
|
|
46334
|
-
console.error(
|
|
46335
|
-
JSON.stringify(
|
|
46336
|
-
{ status: "error", error: "Browser login requires TTY mode. Use --api-key instead." },
|
|
46337
|
-
null,
|
|
46338
|
-
2
|
|
46339
|
-
)
|
|
46340
|
-
);
|
|
46829
|
+
console.error(JSON.stringify(NON_TTY_LOGIN_GUIDANCE, null, 2));
|
|
46341
46830
|
process.exit(1);
|
|
46342
46831
|
}
|
|
46343
46832
|
const App = () => {
|
|
@@ -46402,17 +46891,34 @@ authCommand.command("status").description("Show authentication status").option("
|
|
|
46402
46891
|
try {
|
|
46403
46892
|
const hasCredentials = await store.hasCredentials();
|
|
46404
46893
|
if (!hasCredentials) {
|
|
46405
|
-
console.log(
|
|
46894
|
+
console.log(
|
|
46895
|
+
JSON.stringify(
|
|
46896
|
+
{
|
|
46897
|
+
status: "unauthenticated",
|
|
46898
|
+
next: "New account: runtype auth register --email <email>. Existing account: runtype auth login --api-key <key>."
|
|
46899
|
+
},
|
|
46900
|
+
null,
|
|
46901
|
+
2
|
|
46902
|
+
)
|
|
46903
|
+
);
|
|
46406
46904
|
return;
|
|
46407
46905
|
}
|
|
46408
46906
|
const credentials = await store.getCredentials();
|
|
46907
|
+
const claimState = await store.getClaimState();
|
|
46409
46908
|
console.log(
|
|
46410
46909
|
JSON.stringify(
|
|
46411
46910
|
{
|
|
46412
46911
|
status: "authenticated",
|
|
46912
|
+
accountState: claimState ? "temporary" : "full",
|
|
46413
46913
|
userId: credentials?.userId,
|
|
46414
46914
|
apiUrl: credentials?.apiUrl || "default",
|
|
46415
|
-
orgId: credentials?.orgId || null
|
|
46915
|
+
orgId: credentials?.orgId || null,
|
|
46916
|
+
...claimState ? {
|
|
46917
|
+
registrationId: claimState.registrationId,
|
|
46918
|
+
credentialExpires: claimState.credentialExpires,
|
|
46919
|
+
claimEmail: claimState.claimEmail ?? null,
|
|
46920
|
+
next: claimState.claimEmail ? "Finish signup: ask the user for the 6-digit code from their email, then run: runtype auth verify <code>" : "Finish signup: ask the user for their email, then run: runtype auth claim <email>"
|
|
46921
|
+
} : {}
|
|
46416
46922
|
},
|
|
46417
46923
|
null,
|
|
46418
46924
|
2
|
|
@@ -46495,6 +47001,7 @@ authCommand.command("export-key").description("Print the stored API key to stdou
|
|
|
46495
47001
|
}
|
|
46496
47002
|
process.stdout.write(apiKey);
|
|
46497
47003
|
});
|
|
47004
|
+
registerAgentAuthCommands(authCommand);
|
|
46498
47005
|
authCommand.command("logout").description("Remove stored credentials").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
46499
47006
|
const store = new CredentialStore();
|
|
46500
47007
|
if (!isTTY(options)) {
|
|
@@ -46557,7 +47064,7 @@ authCommand.command("whoami").description("Display current user information").op
|
|
|
46557
47064
|
if (!isTTY(options) || options.json) {
|
|
46558
47065
|
console.log(JSON.stringify({ status: "unauthenticated" }, null, 2));
|
|
46559
47066
|
} else {
|
|
46560
|
-
console.log(
|
|
47067
|
+
console.log(chalk2.yellow("Not logged in"));
|
|
46561
47068
|
console.log("Run: runtype auth login");
|
|
46562
47069
|
}
|
|
46563
47070
|
return;
|
|
@@ -46649,17 +47156,17 @@ import { useState as useState3, useEffect as useEffect4 } from "react";
|
|
|
46649
47156
|
|
|
46650
47157
|
// src/lib/file-utils.ts
|
|
46651
47158
|
import { readFileSync as readFileSync3 } from "fs";
|
|
46652
|
-
import
|
|
47159
|
+
import chalk3 from "chalk";
|
|
46653
47160
|
function parseJsonObject(raw, label) {
|
|
46654
47161
|
let parsed;
|
|
46655
47162
|
try {
|
|
46656
47163
|
parsed = JSON.parse(raw);
|
|
46657
47164
|
} catch {
|
|
46658
|
-
console.error(
|
|
47165
|
+
console.error(chalk3.red(`Invalid JSON in ${label}`));
|
|
46659
47166
|
process.exit(1);
|
|
46660
47167
|
}
|
|
46661
47168
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
46662
|
-
console.error(
|
|
47169
|
+
console.error(chalk3.red(`${label} must be a JSON object`));
|
|
46663
47170
|
process.exit(1);
|
|
46664
47171
|
}
|
|
46665
47172
|
return parsed;
|
|
@@ -46670,7 +47177,7 @@ function readJsonFile(path20, opts = {}) {
|
|
|
46670
47177
|
try {
|
|
46671
47178
|
raw = readFileSync3(path20, "utf-8");
|
|
46672
47179
|
} catch {
|
|
46673
|
-
console.error(
|
|
47180
|
+
console.error(chalk3.red(`Failed to read ${label}: ${path20}`));
|
|
46674
47181
|
process.exit(1);
|
|
46675
47182
|
}
|
|
46676
47183
|
const obj = parseJsonObject(raw, label);
|
|
@@ -46684,11 +47191,11 @@ function readJsonFile(path20, opts = {}) {
|
|
|
46684
47191
|
}
|
|
46685
47192
|
function resolveJsonPayload(inline, fromFile, label) {
|
|
46686
47193
|
if (inline !== void 0 && fromFile !== void 0) {
|
|
46687
|
-
console.error(
|
|
47194
|
+
console.error(chalk3.red(`Error: provide only one of inline JSON or --from-file for ${label}`));
|
|
46688
47195
|
process.exit(1);
|
|
46689
47196
|
}
|
|
46690
47197
|
if (inline === void 0 && fromFile === void 0) {
|
|
46691
|
-
console.error(
|
|
47198
|
+
console.error(chalk3.red(`Error: ${label} is required \u2014 pass inline JSON or --from-file <path>`));
|
|
46692
47199
|
process.exit(1);
|
|
46693
47200
|
}
|
|
46694
47201
|
if (fromFile !== void 0) {
|
|
@@ -46705,7 +47212,7 @@ import {
|
|
|
46705
47212
|
// src/lib/ensure-auth.ts
|
|
46706
47213
|
init_credential_store();
|
|
46707
47214
|
import readline from "readline";
|
|
46708
|
-
import
|
|
47215
|
+
import chalk4 from "chalk";
|
|
46709
47216
|
async function promptConfirm(message, options) {
|
|
46710
47217
|
const defaultYes = options?.default !== false;
|
|
46711
47218
|
const hint = defaultYes ? "Y/n" : "y/N";
|
|
@@ -46715,7 +47222,7 @@ async function promptConfirm(message, options) {
|
|
|
46715
47222
|
terminal: true
|
|
46716
47223
|
});
|
|
46717
47224
|
return new Promise((resolve13) => {
|
|
46718
|
-
rl.question(
|
|
47225
|
+
rl.question(chalk4.cyan(`${message} (${hint}): `), (answer) => {
|
|
46719
47226
|
rl.close();
|
|
46720
47227
|
const trimmed2 = answer.trim().toLowerCase();
|
|
46721
47228
|
if (trimmed2 === "") {
|
|
@@ -46744,9 +47251,9 @@ async function ensureAuth(options) {
|
|
|
46744
47251
|
return apiKey;
|
|
46745
47252
|
}
|
|
46746
47253
|
if (options?.interactive === false) {
|
|
46747
|
-
console.error(
|
|
47254
|
+
console.error(chalk4.yellow("\nAuthentication required."));
|
|
46748
47255
|
console.error(
|
|
46749
|
-
|
|
47256
|
+
chalk4.gray(
|
|
46750
47257
|
'Run "runtype auth login" first or set RUNTYPE_API_KEY before reading command input from stdin.'
|
|
46751
47258
|
)
|
|
46752
47259
|
);
|
|
@@ -46755,10 +47262,10 @@ async function ensureAuth(options) {
|
|
|
46755
47262
|
}
|
|
46756
47263
|
return null;
|
|
46757
47264
|
}
|
|
46758
|
-
console.log(
|
|
47265
|
+
console.log(chalk4.yellow("\nAuthentication required."));
|
|
46759
47266
|
const shouldLogin = await promptConfirm("Would you like to log in now?", { default: true });
|
|
46760
47267
|
if (!shouldLogin) {
|
|
46761
|
-
console.log(
|
|
47268
|
+
console.log(chalk4.gray(`Run "runtype auth login" when you're ready.`));
|
|
46762
47269
|
if (options?.exitOnFailure !== false) {
|
|
46763
47270
|
process.exit(1);
|
|
46764
47271
|
}
|
|
@@ -46767,100 +47274,37 @@ async function ensureAuth(options) {
|
|
|
46767
47274
|
return handleBrowserLogin(store, options?.apiUrl);
|
|
46768
47275
|
}
|
|
46769
47276
|
async function handleBrowserLogin(store, apiUrl) {
|
|
46770
|
-
console.log(
|
|
47277
|
+
console.log(chalk4.gray("Opening browser for authentication..."));
|
|
46771
47278
|
try {
|
|
46772
47279
|
const oauth = new OAuthManager({
|
|
46773
47280
|
clerkPublishableKey: getClerkPublishableKey(),
|
|
46774
47281
|
dashboardUrl: getDashboardUrl()
|
|
46775
47282
|
});
|
|
46776
|
-
console.log(
|
|
47283
|
+
console.log(chalk4.gray("Waiting for browser authentication..."));
|
|
46777
47284
|
const sessionToken = await oauth.authenticate("sign-in");
|
|
46778
|
-
console.log(
|
|
47285
|
+
console.log(chalk4.gray("Getting API key..."));
|
|
46779
47286
|
const apiKeyManager = new ApiKeyManager();
|
|
46780
47287
|
const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
|
|
46781
47288
|
sessionToken,
|
|
46782
47289
|
apiUrl || getApiUrl()
|
|
46783
47290
|
);
|
|
46784
|
-
console.log(
|
|
47291
|
+
console.log(chalk4.gray("Storing credentials..."));
|
|
46785
47292
|
await store.saveCredentials({
|
|
46786
47293
|
apiKey: key,
|
|
46787
47294
|
userId,
|
|
46788
47295
|
orgId,
|
|
46789
47296
|
apiUrl: apiUrl || getApiUrl()
|
|
46790
47297
|
});
|
|
46791
|
-
console.log(
|
|
47298
|
+
console.log(chalk4.green("Logged in successfully!"));
|
|
46792
47299
|
return key;
|
|
46793
47300
|
} catch (error51) {
|
|
46794
47301
|
const message = error51 instanceof Error ? error51.message : "Unknown error";
|
|
46795
|
-
console.log(
|
|
46796
|
-
console.error(
|
|
47302
|
+
console.log(chalk4.red("Login failed"));
|
|
47303
|
+
console.error(chalk4.red(message));
|
|
46797
47304
|
return null;
|
|
46798
47305
|
}
|
|
46799
47306
|
}
|
|
46800
47307
|
|
|
46801
|
-
// src/lib/output.ts
|
|
46802
|
-
import chalk4 from "chalk";
|
|
46803
|
-
function printJson(data) {
|
|
46804
|
-
console.log(JSON.stringify(data, null, 2));
|
|
46805
|
-
}
|
|
46806
|
-
function printList(items, options) {
|
|
46807
|
-
console.log(chalk4.cyan(`${options.title}:`));
|
|
46808
|
-
if (items.length === 0) {
|
|
46809
|
-
console.log(chalk4.gray(` ${options.emptyMessage || "None found"}`));
|
|
46810
|
-
return;
|
|
46811
|
-
}
|
|
46812
|
-
for (const item of items) {
|
|
46813
|
-
const parts = [];
|
|
46814
|
-
for (const col of options.columns) {
|
|
46815
|
-
const value = item[col.key];
|
|
46816
|
-
if (value === void 0 || value === null) continue;
|
|
46817
|
-
const str = String(value);
|
|
46818
|
-
const colorFn = col.color ? chalk4[col.color] : (s) => s;
|
|
46819
|
-
if (col.label) {
|
|
46820
|
-
parts.push(`${col.label}: ${colorFn(str)}`);
|
|
46821
|
-
} else {
|
|
46822
|
-
parts.push(colorFn(str));
|
|
46823
|
-
}
|
|
46824
|
-
}
|
|
46825
|
-
console.log(` ${parts.join(" - ")}`);
|
|
46826
|
-
}
|
|
46827
|
-
if (options.total !== void 0) {
|
|
46828
|
-
console.log(chalk4.dim(`
|
|
46829
|
-
Total: ${options.total}`));
|
|
46830
|
-
}
|
|
46831
|
-
}
|
|
46832
|
-
function printDetail(title, fields) {
|
|
46833
|
-
console.log(chalk4.cyan(`${title}:`));
|
|
46834
|
-
for (const field of fields) {
|
|
46835
|
-
if (field.value === void 0 || field.value === null) continue;
|
|
46836
|
-
const str = typeof field.value === "object" ? JSON.stringify(field.value) : String(field.value);
|
|
46837
|
-
console.log(` ${chalk4.gray(field.label + ":")} ${str}`);
|
|
46838
|
-
}
|
|
46839
|
-
}
|
|
46840
|
-
function getTotalCount(pagination) {
|
|
46841
|
-
return pagination?.totalCount ?? pagination?.total ?? void 0;
|
|
46842
|
-
}
|
|
46843
|
-
function buildListParams(options, extra) {
|
|
46844
|
-
const params = {};
|
|
46845
|
-
if (options.limit) params.limit = options.limit;
|
|
46846
|
-
if (options.cursor) params.cursor = options.cursor;
|
|
46847
|
-
if (extra) {
|
|
46848
|
-
for (const [key, value] of Object.entries(extra)) {
|
|
46849
|
-
if (value !== void 0) params[key] = value;
|
|
46850
|
-
}
|
|
46851
|
-
}
|
|
46852
|
-
return params;
|
|
46853
|
-
}
|
|
46854
|
-
function printNextCursorHint(commandPath, pagination) {
|
|
46855
|
-
const hasMore = pagination?.hasMore ?? pagination?.hasNextPage ?? false;
|
|
46856
|
-
const nextCursor = pagination?.nextCursor !== void 0 ? pagination.nextCursor : pagination?.cursor ?? null;
|
|
46857
|
-
if (!hasMore || !nextCursor) return;
|
|
46858
|
-
console.log(
|
|
46859
|
-
chalk4.yellow("\n More results available \u2014 fetch the next page with:") + chalk4.dim(`
|
|
46860
|
-
${commandPath} --cursor ${nextCursor}`)
|
|
46861
|
-
);
|
|
46862
|
-
}
|
|
46863
|
-
|
|
46864
47308
|
// src/commands/flows.ts
|
|
46865
47309
|
function flowsListParams(options) {
|
|
46866
47310
|
return {
|
|
@@ -72832,32 +73276,54 @@ import chalk42 from "chalk";
|
|
|
72832
73276
|
// ../shared/dist/agent-onboarding-content.mjs
|
|
72833
73277
|
var AGENT_ONBOARDING_MARKDOWN = `# Runtype for Coding Agents
|
|
72834
73278
|
|
|
72835
|
-
You are reading the setup script served by \`https://runtype.ai\`. Your durable goal is to leave this coding harness connected to Runtype's hosted MCP server
|
|
73279
|
+
You are reading the setup script served by \`https://runtype.ai\`. Your durable goal is to leave this coding harness connected to Runtype's hosted MCP server. Use the Runtype CLI as a bridge during this first session; do not treat it as the final integration.
|
|
73280
|
+
|
|
73281
|
+
Signup never requires a browser. The only things you may ask the user for are their email address and the 6-digit code Runtype emails them. Do not tell the user to open a dashboard, sign-in page, or OAuth window during signup.
|
|
72836
73282
|
|
|
72837
73283
|
## Golden path \u2014 do exactly this
|
|
72838
73284
|
|
|
72839
73285
|
If the live Runtype MCP tools are already available in this session, skip setup and go to "After MCP is active" below. Otherwise follow every step in order:
|
|
72840
73286
|
|
|
72841
|
-
### 1. Install and
|
|
73287
|
+
### 1. Install the CLI and check auth
|
|
72842
73288
|
|
|
72843
73289
|
\`\`\`bash
|
|
72844
73290
|
npm install -g @runtypelabs/cli
|
|
72845
|
-
runtype auth
|
|
73291
|
+
runtype auth status
|
|
72846
73292
|
\`\`\`
|
|
72847
73293
|
|
|
72848
|
-
|
|
73294
|
+
If status reports \`authenticated\` with \`accountState: "full"\`, skip to step 3. If it reports a pending signup, its \`next\` field tells you the exact command to resume with.
|
|
72849
73295
|
|
|
72850
|
-
### 2.
|
|
73296
|
+
### 2. Sign the user up from the terminal
|
|
73297
|
+
|
|
73298
|
+
Ask the user one question: "What email address should I use for your Runtype account?" Then run:
|
|
72851
73299
|
|
|
72852
73300
|
\`\`\`bash
|
|
72853
|
-
runtype
|
|
73301
|
+
runtype auth register --email <their-email>
|
|
72854
73302
|
\`\`\`
|
|
72855
73303
|
|
|
72856
|
-
This
|
|
73304
|
+
This creates the account and emails the user a 6-digit code. Ask the user: "Runtype emailed a 6-digit code to <their-email> \u2014 what is it?" Then run:
|
|
73305
|
+
|
|
73306
|
+
\`\`\`bash
|
|
73307
|
+
runtype auth verify <code>
|
|
73308
|
+
\`\`\`
|
|
72857
73309
|
|
|
72858
|
-
|
|
73310
|
+
That completes signup. Every command in this flow prints JSON with a \`next\` field \u2014 follow it. Notes:
|
|
72859
73311
|
|
|
72860
|
-
|
|
73312
|
+
- These commands work without a TTY and never open a browser.
|
|
73313
|
+
- \`register\` is safe to re-run; it resumes a pending signup instead of creating a duplicate account.
|
|
73314
|
+
- After \`register\` (before \`verify\`) you already hold a temporary credential: CLI commands work with restricted models and quota, and \`verify\` upgrades the same stored login in place. Do not build anything substantial until \`verify\` succeeds.
|
|
73315
|
+
- If the email already has a Runtype account, the command reports \`email_already_registered\`. In that case ask the user for an API key from their dashboard (Settings > API keys) and run \`runtype auth login --api-key <key>\` instead. This is the only path where the user touches the dashboard, and only because they already have an account.
|
|
73316
|
+
- A wrong code returns \`otp_invalid\` \u2014 ask the user to re-check and run \`runtype auth verify\` again. An expired code returns \`otp_expired\` \u2014 re-send with \`runtype auth claim <email>\`. Codes expire after 10 minutes and lock after 5 wrong attempts, so confirm the code with the user rather than guessing; after a lockout or expiry, re-send with \`runtype auth claim <email>\`.
|
|
73317
|
+
|
|
73318
|
+
### 3. Install skills and the MCP connection
|
|
73319
|
+
|
|
73320
|
+
\`\`\`bash
|
|
73321
|
+
runtype install-mcp
|
|
73322
|
+
\`\`\`
|
|
73323
|
+
|
|
73324
|
+
This command detects the current coding harness, installs the Runtype agent skills for it, and writes the user-level hosted MCP configuration. If it asks which harness to configure, select the harness running this session. If it reports an OAuth action for the harness's own MCP connection, relay that instruction to the user for later \u2014 it happens on the harness's schedule, not during signup. That harness-side OAuth may open a browser when the user runs it; that is fine and expected \u2014 the no-browser rule above applies to Runtype signup, not to the harness's own connection ceremony.
|
|
73325
|
+
|
|
73326
|
+
### 4. Keep working in this session with the CLI
|
|
72861
73327
|
|
|
72862
73328
|
Do not stop just because the new MCP connection is not visible yet. Use CLI commands for useful first-session work:
|
|
72863
73329
|
|
|
@@ -72869,19 +73335,19 @@ runtype products list
|
|
|
72869
73335
|
runtype --help
|
|
72870
73336
|
\`\`\`
|
|
72871
73337
|
|
|
72872
|
-
The CLI also supports dispatch, records, validation, product setup, and other account operations. Read command help before mutating resources, and report concrete IDs and URLs to the user.
|
|
73338
|
+
The CLI also supports dispatch, records, validation, product setup, and other account operations. Read command help before mutating resources, and report concrete IDs and URLs to the user. If you need Runtype MCP tools before the restart, the CLI bridges them: \`runtype mcp tools\` lists them and \`runtype mcp call <tool>\` invokes one \u2014 same hosted tools, no restart needed.
|
|
72873
73339
|
|
|
72874
|
-
###
|
|
73340
|
+
### 5. Tell the user to restart or reload the harness
|
|
72875
73341
|
|
|
72876
|
-
Before finishing this session, read the final status and next steps from \`runtype install-mcp\`.
|
|
73342
|
+
Before finishing this session, read the final status and next steps from \`runtype install-mcp\`. Retry any failed skill installation. Do not call setup complete while the command reports \`action-required\`, \`partial\`, or an error.
|
|
72877
73343
|
|
|
72878
|
-
Only after
|
|
73344
|
+
Only after signup is verified and skills are installed, tell the user exactly what changed and ask them to restart or reload the coding harness. Use direct language such as:
|
|
72879
73345
|
|
|
72880
|
-
> Runtype
|
|
73346
|
+
> Your Runtype account is verified and the skills plus MCP connection are installed. Please restart or reload this coding agent (or start a new session) so it can activate the new MCP tools. I used the CLI to keep this first session productive; after restart I will use MCP by default.
|
|
72881
73347
|
|
|
72882
73348
|
If an action remains, report that exact action instead of using the success wording above. Do not claim the running agent can use MCP until its Runtype tools actually appear.
|
|
72883
73349
|
|
|
72884
|
-
###
|
|
73350
|
+
### 6. Switch to MCP after restart
|
|
72885
73351
|
|
|
72886
73352
|
In the next session, use Runtype MCP first. Do not fall back to the CLI just because it worked previously. Start by calling:
|
|
72887
73353
|
|
|
@@ -72907,13 +73373,21 @@ runtype install-mcp --agent opencode
|
|
|
72907
73373
|
runtype install-mcp --agent vscode
|
|
72908
73374
|
\`\`\`
|
|
72909
73375
|
|
|
72910
|
-
Use only the line for the current harness. Follow the command's
|
|
73376
|
+
Use only the line for the current harness. Follow the command's restart instructions. Use \`--force\` only when the command reports that an existing MCP server named \`runtype\` points to a different URL and the user agrees to replace it.
|
|
73377
|
+
|
|
73378
|
+
## Auth reference
|
|
73379
|
+
|
|
73380
|
+
The signup commands wrap the agent auth protocol documented at \`https://runtype.com/auth.md\` (anonymous registration plus an email claim ceremony). You normally never need the raw protocol \u2014 the CLI is the supported client:
|
|
72911
73381
|
|
|
72912
|
-
|
|
73382
|
+
- \`runtype auth register --email <email>\` \u2014 create the account and send the code (one step)
|
|
73383
|
+
- \`runtype auth claim <email>\` \u2014 re-send or change the email for a pending signup
|
|
73384
|
+
- \`runtype auth verify <code>\` \u2014 finish signup with the 6-digit code
|
|
73385
|
+
- \`runtype auth login --api-key <key>\` \u2014 existing accounts only
|
|
73386
|
+
- \`runtype auth status\` / \`runtype auth whoami\` \u2014 inspect the current login
|
|
72913
73387
|
|
|
72914
|
-
|
|
73388
|
+
A user-supplied \`RUNTYPE_API_KEY\` environment variable also authenticates every CLI command without a stored login. Never print a credential into the transcript or write one into a committed file.
|
|
72915
73389
|
|
|
72916
|
-
|
|
73390
|
+
Browser login (\`runtype auth login\` with no flags) exists for humans running the CLI in their own interactive terminal. Do not use it from a coding harness.
|
|
72917
73391
|
|
|
72918
73392
|
## After MCP is active
|
|
72919
73393
|
|
|
@@ -72932,7 +73406,7 @@ API: [api.runtype.com](https://api.runtype.com)
|
|
|
72932
73406
|
|
|
72933
73407
|
// src/commands/onboard.ts
|
|
72934
73408
|
init_credential_store();
|
|
72935
|
-
var AUTH_LOGIN_INSTRUCTION = "Sign in to Runtype:
|
|
73409
|
+
var AUTH_LOGIN_INSTRUCTION = "Sign in to Runtype first. New account: `runtype auth register --email <email>` then `runtype auth verify <code>`. Existing account: `runtype auth login --api-key <key>` (or set RUNTYPE_API_KEY). Then re-run `runtype onboard`.";
|
|
72936
73410
|
var AFTER_RESTART_STEPS = [
|
|
72937
73411
|
"Use the Runtype MCP server first for all Runtype work.",
|
|
72938
73412
|
"Start with the get_build_instructions tool before creating or changing resources.",
|
|
@@ -73217,7 +73691,7 @@ function collectBundleFiles(dir) {
|
|
|
73217
73691
|
walk(dir);
|
|
73218
73692
|
return zippable;
|
|
73219
73693
|
}
|
|
73220
|
-
function
|
|
73694
|
+
function fail2(message, jsonMode, code) {
|
|
73221
73695
|
if (jsonMode) {
|
|
73222
73696
|
printJson({ status: "error", error: message, code });
|
|
73223
73697
|
} else {
|
|
@@ -73256,7 +73730,7 @@ appsCommand.command("list").description("List your apps").option("--json", "Stru
|
|
|
73256
73730
|
console.log(` ${app.url} ${status} ${app.visibility}${serving}`);
|
|
73257
73731
|
}
|
|
73258
73732
|
} catch (error51) {
|
|
73259
|
-
|
|
73733
|
+
fail2(
|
|
73260
73734
|
`Failed to list apps: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73261
73735
|
jsonMode,
|
|
73262
73736
|
"LIST_FAILED"
|
|
@@ -73285,7 +73759,7 @@ appsCommand.command("create").description("Create an app (the hostname is {slug}
|
|
|
73285
73759
|
console.log(` ${app.url}`);
|
|
73286
73760
|
console.log(chalk43.dim(" Deploy a bundle with `runtype apps deploy <dir> --app " + app.id + "`"));
|
|
73287
73761
|
} catch (error51) {
|
|
73288
|
-
|
|
73762
|
+
fail2(
|
|
73289
73763
|
`Failed to create app: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73290
73764
|
jsonMode,
|
|
73291
73765
|
"CREATE_FAILED"
|
|
@@ -73300,27 +73774,27 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
73300
73774
|
const apiKey = await ensureAuth();
|
|
73301
73775
|
if (!apiKey) return;
|
|
73302
73776
|
if (!existsSync17(dir) || !statSync7(dir).isDirectory()) {
|
|
73303
|
-
|
|
73777
|
+
fail2(`Not a directory: ${dir}`, jsonMode, "INVALID_DIR");
|
|
73304
73778
|
}
|
|
73305
73779
|
let bundleFiles = {};
|
|
73306
73780
|
try {
|
|
73307
73781
|
bundleFiles = collectBundleFiles(dir);
|
|
73308
73782
|
} catch (error51) {
|
|
73309
|
-
|
|
73783
|
+
fail2(
|
|
73310
73784
|
`Failed to read ${dir}: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73311
73785
|
jsonMode,
|
|
73312
73786
|
"INVALID_DIR"
|
|
73313
73787
|
);
|
|
73314
73788
|
}
|
|
73315
73789
|
if (!bundleFiles["index.html"]) {
|
|
73316
|
-
|
|
73790
|
+
fail2(
|
|
73317
73791
|
`${dir} is missing index.html at its root (symlinked files are not bundled).`,
|
|
73318
73792
|
jsonMode,
|
|
73319
73793
|
"MISSING_INDEX"
|
|
73320
73794
|
);
|
|
73321
73795
|
}
|
|
73322
73796
|
if (!bundleFiles[MANIFEST_FILENAME]) {
|
|
73323
|
-
|
|
73797
|
+
fail2(
|
|
73324
73798
|
`${dir} is missing ${MANIFEST_FILENAME} at its root (symlinked files are not bundled). Every app bundle needs a manifest; see the build-runtype-app skill or the Runtype docs.`,
|
|
73325
73799
|
jsonMode,
|
|
73326
73800
|
"MISSING_MANIFEST"
|
|
@@ -73368,7 +73842,7 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
73368
73842
|
console.log(chalk43.dim(` Uploaded but not activated. Activate with \`runtype apps activate ${options.app} ${version2.id}\``));
|
|
73369
73843
|
}
|
|
73370
73844
|
} catch (error51) {
|
|
73371
|
-
|
|
73845
|
+
fail2(
|
|
73372
73846
|
`Deploy failed: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73373
73847
|
jsonMode,
|
|
73374
73848
|
"DEPLOY_FAILED"
|
|
@@ -73399,7 +73873,7 @@ appsCommand.command("versions <appId>").description("List an app's versions, new
|
|
|
73399
73873
|
);
|
|
73400
73874
|
}
|
|
73401
73875
|
} catch (error51) {
|
|
73402
|
-
|
|
73876
|
+
fail2(
|
|
73403
73877
|
`Failed to list versions: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73404
73878
|
jsonMode,
|
|
73405
73879
|
"VERSIONS_FAILED"
|
|
@@ -73421,7 +73895,7 @@ appsCommand.command("activate <appId> <versionId>").description("Activate an upl
|
|
|
73421
73895
|
console.log(chalk43.green(`Activated version ${version2.versionNumber}`));
|
|
73422
73896
|
console.log(` Live at ${chalk43.bold(app.url)}`);
|
|
73423
73897
|
} catch (error51) {
|
|
73424
|
-
|
|
73898
|
+
fail2(
|
|
73425
73899
|
`Activation failed: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73426
73900
|
jsonMode,
|
|
73427
73901
|
"ACTIVATE_FAILED"
|
|
@@ -73442,7 +73916,7 @@ appsCommand.command("delete <appId>").description("Permanently delete an app, it
|
|
|
73442
73916
|
}
|
|
73443
73917
|
console.log(chalk43.green(`Deleted ${appId}`));
|
|
73444
73918
|
} catch (error51) {
|
|
73445
|
-
|
|
73919
|
+
fail2(
|
|
73446
73920
|
`Delete failed: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
73447
73921
|
jsonMode,
|
|
73448
73922
|
"DELETE_FAILED"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.31.0",
|
|
4
4
|
"description": "Command-line interface for Runtype AI platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"rosie-skills": "0.8.1",
|
|
27
27
|
"yaml": "^2.9.0",
|
|
28
28
|
"@runtypelabs/ink-components": "0.3.5",
|
|
29
|
-
"@runtypelabs/sdk": "6.1.
|
|
29
|
+
"@runtypelabs/sdk": "6.1.4",
|
|
30
30
|
"@runtypelabs/terminal-animations": "0.2.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"tsx": "^4.7.1",
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
43
|
"vitest": "^4.1.0",
|
|
44
|
-
"@runtypelabs/shared": "3.
|
|
44
|
+
"@runtypelabs/shared": "3.5.0"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=22.12.0"
|