@runtypelabs/cli 2.59.0 → 2.59.2
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/chunk-0-T9NWjH.js +3899 -0
- package/dist/chunk-0x7UKC1U.js +17 -0
- package/dist/chunk-2ySxXtcg.js +418 -0
- package/dist/chunk-3An_BRHv.js +501 -0
- package/dist/chunk-6uTxgdiY.js +16218 -0
- package/dist/chunk-87nRnNRG.js +1363 -0
- package/dist/chunk-B3wo8uRx.js +322 -0
- package/dist/chunk-BGp3ZdBy.js +560 -0
- package/dist/chunk-BJQv9Elp.js +107 -0
- package/dist/chunk-BOpO6xE-.js +6053 -0
- package/dist/chunk-BQXXCL8B.js +102 -0
- package/dist/chunk-BV-8isOd.js +34 -0
- package/dist/chunk-BVMhjGVe.js +826 -0
- package/dist/chunk-BW2fg46T.js +141 -0
- package/dist/chunk-BWJdY5Ag.js +36 -0
- package/dist/chunk-BYJOrLhh.js +302 -0
- package/dist/chunk-BYab8EVL.js +127 -0
- package/dist/chunk-BfEKvp7h.js +82 -0
- package/dist/chunk-BhgLSfH6.js +1123 -0
- package/dist/chunk-BirUxTtH.js +296 -0
- package/dist/chunk-BmB6UVZ9.js +1559 -0
- package/dist/chunk-BnWhIqW1.js +109 -0
- package/dist/chunk-Bpfnfk0t.js +123 -0
- package/dist/chunk-BrhgdnM3.js +108 -0
- package/dist/chunk-Bt0XAgX5.js +327 -0
- package/dist/chunk-Bu0FJr6S.js +84 -0
- package/dist/chunk-BvEoKpN3.js +255 -0
- package/dist/chunk-BxNJgYbE.js +28 -0
- package/dist/chunk-C1OFhd7F.js +6131 -0
- package/dist/chunk-C7e7WwbU.js +331 -0
- package/dist/chunk-CDTYP6bN.js +558 -0
- package/dist/chunk-CGDQoqDC.js +24 -0
- package/dist/chunk-CJraEBVd.js +127 -0
- package/dist/chunk-CQrNhOtv.js +40 -0
- package/dist/chunk-CW6AzBH6.js +128 -0
- package/dist/chunk-CXgL4w-9.js +317 -0
- package/dist/chunk-CZrqoPyf.js +233 -0
- package/dist/chunk-CcFCyqbB.js +83 -0
- package/dist/chunk-CdaIvO-y.js +485 -0
- package/dist/chunk-CehSXtgK.js +624 -0
- package/dist/chunk-Ciqkop65.js +218 -0
- package/dist/chunk-CoAWdSiE.js +43 -0
- package/dist/chunk-Crb4y7t4.js +830 -0
- package/dist/chunk-CrlqIfmb.js +337 -0
- package/dist/chunk-Cs4pwCQ-.js +324 -0
- package/dist/chunk-CwM_9y01.js +519 -0
- package/dist/chunk-CyRbIJg8.js +440 -0
- package/dist/chunk-D0OHsyMy.js +212 -0
- package/dist/chunk-D3y2bURs.js +1347 -0
- package/dist/chunk-D4_mJk03.js +2338 -0
- package/dist/chunk-D6C8rned.js +578 -0
- package/dist/chunk-D9ZbUnks.js +11 -0
- package/dist/chunk-DEPmLshX.js +834 -0
- package/dist/chunk-DFj1UPB2.js +330 -0
- package/dist/chunk-DGdFdZVf.js +197 -0
- package/dist/chunk-DGv7X9S_.js +63 -0
- package/dist/chunk-DJQ47WYZ.js +664 -0
- package/dist/chunk-DJw3O61j.js +1974 -0
- package/dist/chunk-DK4FbWVK.js +100 -0
- package/dist/chunk-DSRYhUus.js +993 -0
- package/dist/chunk-DajZ9Eg4.js +299 -0
- package/dist/chunk-Df4dunvU.js +195 -0
- package/dist/chunk-DjNTPhfO.js +481 -0
- package/dist/chunk-Dn8eaJuu.js +1042 -0
- package/dist/chunk-DwDWqSxM.js +637 -0
- package/dist/chunk-Dz7cEtxl.js +84 -0
- package/dist/chunk-E_0bgjrS.js +359 -0
- package/dist/chunk-KPYN2vUp.js +776 -0
- package/dist/chunk-KcqPQJBi.js +86 -0
- package/dist/chunk-N9H0JnoG.js +349 -0
- package/dist/chunk-NONFcXkn.js +728 -0
- package/dist/chunk-PrFjcpmH.js +256 -0
- package/dist/chunk-QSfNYiw9.js +11 -0
- package/dist/chunk-R265bzj9.js +226 -0
- package/dist/chunk-X9V_r6jM.js +166 -0
- package/dist/chunk-ZBtPUtyA.js +55 -0
- package/dist/chunk-ZCTfmOs7.js +4939 -0
- package/dist/chunk-h6oavj0t.js +97 -0
- package/dist/chunk-i7DwkvvD.js +42 -0
- package/dist/chunk-mG-4eZ6S.js +203 -0
- package/dist/chunk-o0ygUbCf.js +313 -0
- package/dist/chunk-ucu5lrDZ.js +5715 -0
- package/dist/index.js +11367 -73764
- package/package.json +6 -6
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { r as createCliClient } from "./chunk-BW2fg46T.js";
|
|
3
|
+
import { n as ensureAuth } from "./chunk-CJraEBVd.js";
|
|
4
|
+
import { a as printJson } from "./chunk-CcFCyqbB.js";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
// src/commands/signals.ts
|
|
9
|
+
const signalsCommand = new Command("signals").description("Configure trace signals: typed fields read out of every bound agent turn");
|
|
10
|
+
async function run(action) {
|
|
11
|
+
try {
|
|
12
|
+
const apiKey = await ensureAuth();
|
|
13
|
+
if (!apiKey) return;
|
|
14
|
+
printJson(await action(createCliClient(apiKey)));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function readDefinition(path) {
|
|
21
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
function positiveNumber(value) {
|
|
24
|
+
const parsed = Number(value);
|
|
25
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`Expected a positive number, got "${value}"`);
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
signalsCommand.command("list").description("List signal sets, newest first").option("--presets", "List the presets `create --preset` accepts instead").action((options) => run((client) => options.presets ? client.signals.listPresets() : client.signals.listSets()));
|
|
29
|
+
signalsCommand.command("get <signalSetId>").description("Show a signal set with every version").action((id) => run((client) => client.signals.getSet(id)));
|
|
30
|
+
signalsCommand.command("create <name>").description("Create a signal set from a preset or a definition file; version 1 is current at once").option("--preset <preset>", "Start from a preset: conversation_health or failure_detector").option("-f, --file <path>", "JSON definition: { signals: { <key>: { type, instructions, ... } } }").option("--description <text>", "What the set watches for").action((name, options) => run(async (client) => client.signals.createSet({
|
|
31
|
+
name,
|
|
32
|
+
...options.description ? { description: options.description } : {},
|
|
33
|
+
...options.preset ? { preset: options.preset } : {},
|
|
34
|
+
...options.file ? { definition: await readDefinition(options.file) } : {}
|
|
35
|
+
})));
|
|
36
|
+
signalsCommand.command("update <signalSetId>").description("Rename or re-describe a signal set").option("--name <name>", "New name").option("--description <text>", "New description").action((id, options) => run((client) => client.signals.updateSet(id, options)));
|
|
37
|
+
signalsCommand.command("delete <signalSetId>").description("Delete a signal set; unbind every binding of it first").action((id) => run((client) => client.signals.deleteSet(id)));
|
|
38
|
+
signalsCommand.command("publish <signalSetId>").description("Publish a new immutable version from a definition file and make it current").requiredOption("-f, --file <path>", "JSON definition").action((id, options) => run(async (client) => client.signals.publishVersion(id, await readDefinition(options.file))));
|
|
39
|
+
signalsCommand.command("preview <signalSetId> <version>").description("Dry-run a version over an agent's recent traces (billed, writes nothing)").requiredOption("--agent <agentId>", "Agent whose recent completed traces to read").option("--limit <n>", "Traces to read (default 20, at most 50)", positiveNumber).option("--acknowledge-external-processing", "Only after a human confirms (PHI organization, model outside Workers AI)").action((id, version, options) => run((client) => client.signals.preview(id, positiveNumber(version), {
|
|
40
|
+
agentId: options.agent,
|
|
41
|
+
...options.limit !== void 0 ? { limit: options.limit } : {},
|
|
42
|
+
...options.acknowledgeExternalProcessing ? { acknowledgeExternalProcessing: true } : {}
|
|
43
|
+
})));
|
|
44
|
+
signalsCommand.command("bindings").description("List signal bindings").option("--agent <agentId>", "Only bindings on this agent").option("--set <signalSetId>", "Only bindings of this set").option("--include-disabled", "Also list unbound bindings").action((options) => run((client) => client.signals.listBindings({
|
|
45
|
+
...options.agent ? { agentId: options.agent } : {},
|
|
46
|
+
...options.set ? { signalSetId: options.set } : {},
|
|
47
|
+
...options.includeDisabled ? { includeDisabled: true } : {}
|
|
48
|
+
})));
|
|
49
|
+
signalsCommand.command("bind <signalSetId>").description("Bind a signal set version to an agent").requiredOption("--agent <agentId>", "Native or external (OTLP) agent id").option("--set-version <n>", "Set version (default: current)", positiveNumber).option("--sample-rate <rate>", "Evaluate this share of matching turns instead of all", positiveNumber).option("--uniform-share <share>", "Share drawn before the filter for unbiased rates (default 0.05)", Number).option("--acknowledge-external-processing", "Only after a human confirms (PHI organization, model outside Workers AI)").action((signalSetId, options) => {
|
|
50
|
+
const input = {
|
|
51
|
+
signalSetId,
|
|
52
|
+
target: {
|
|
53
|
+
kind: "agent",
|
|
54
|
+
id: options.agent
|
|
55
|
+
},
|
|
56
|
+
...options.setVersion !== void 0 ? { versionNumber: options.setVersion } : {},
|
|
57
|
+
...options.sampleRate !== void 0 ? { coverage: {
|
|
58
|
+
mode: "sample",
|
|
59
|
+
rate: options.sampleRate
|
|
60
|
+
} } : {},
|
|
61
|
+
...options.uniformShare !== void 0 ? { uniformShare: options.uniformShare } : {},
|
|
62
|
+
...options.acknowledgeExternalProcessing ? { acknowledgeExternalProcessing: true } : {}
|
|
63
|
+
};
|
|
64
|
+
return run((client) => client.signals.bind(input));
|
|
65
|
+
});
|
|
66
|
+
signalsCommand.command("unbind <bindingId>").description("Disable a signal binding").action((id) => run((client) => client.signals.unbind(id)));
|
|
67
|
+
signalsCommand.command("stats").description("Signal rollups over a window (default: the last 7 days)").option("--agent <agentId>", "Only this agent").option("--binding <bindingId>", "Only this binding").option("--sample <sample>", "uniform (live-traffic rates) or filtered").option("--from <iso>", "Window start").option("--to <iso>", "Window end").action((options) => run((client) => client.signals.stats({
|
|
68
|
+
...options.agent ? { agentId: options.agent } : {},
|
|
69
|
+
...options.binding ? { bindingId: options.binding } : {},
|
|
70
|
+
...options.sample === "uniform" || options.sample === "filtered" ? { sample: options.sample } : {},
|
|
71
|
+
...options.from ? { from: options.from } : {},
|
|
72
|
+
...options.to ? { to: options.to } : {}
|
|
73
|
+
})));
|
|
74
|
+
const runsCommand = new Command("runs").description("List agent and flow runs");
|
|
75
|
+
runsCommand.command("list").description("List runs, newest first").option("--agent <agentId>", "Only runs of this agent").option("--status <status>", "running, completed or failed").option("--signal <signal>", "Only runs where a signal fired: key, or key:value").option("--conversation <conversationId>", "Only runs of this conversation").option("--limit <n>", "Page size", positiveNumber).option("--cursor <cursor>", "Continue from a previous page").action((options) => run((client) => client.get("/runs", {
|
|
76
|
+
...options.agent ? { agentId: options.agent } : {},
|
|
77
|
+
...options.status ? { status: options.status } : {},
|
|
78
|
+
...options.signal ? { signal: options.signal } : {},
|
|
79
|
+
...options.conversation ? { conversationId: options.conversation } : {},
|
|
80
|
+
...options.limit !== void 0 ? { limit: options.limit } : {},
|
|
81
|
+
...options.cursor ? { cursor: options.cursor } : {}
|
|
82
|
+
})));
|
|
83
|
+
export { runsCommand, signalsCommand };
|
|
84
|
+
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { b as CredentialStore, t as isTTY } from "./index.js";
|
|
3
|
+
import { l as getRuntimeApiKey } from "./chunk-BW2fg46T.js";
|
|
4
|
+
import { n as ensureAuth } from "./chunk-CJraEBVd.js";
|
|
5
|
+
import { a as printJson } from "./chunk-CcFCyqbB.js";
|
|
6
|
+
import { a as getMcpSkillsAgentId, i as getMcpRestartInstruction, installSkillsForAgent, n as authenticateMcpAgent, o as selectAgent, r as configureMcpAgent, t as MCP_AGENT_DEFINITIONS } from "./chunk-DJQ47WYZ.js";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { existsSync, readFileSync } from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
// ../shared/dist/agent-onboarding-content.mjs
|
|
12
|
+
const AGENT_ONBOARDING_MARKDOWN = `# Runtype for Coding Agents
|
|
13
|
+
|
|
14
|
+
Use this guide when the user asks to connect their coding harness to Runtype. If they only ask what Runtype does, explain it without installing software or creating an account. During setup, use the CLI until the hosted MCP tools are available.
|
|
15
|
+
|
|
16
|
+
Signup never requires a browser. Ask only for the email address and emailed 6-digit code during new-account verification. Do not tell the user to open a dashboard, sign-in page, or OAuth window during signup. Existing-account recovery and the harness's MCP OAuth are separate paths below.
|
|
17
|
+
|
|
18
|
+
## Golden path — do exactly this
|
|
19
|
+
|
|
20
|
+
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:
|
|
21
|
+
|
|
22
|
+
### 1. Install the CLI and check auth
|
|
23
|
+
|
|
24
|
+
\`\`\`bash
|
|
25
|
+
npm install -g @runtypelabs/cli
|
|
26
|
+
runtype auth status
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Reuse an installed CLI rather than reinstalling it. If status reports \`authenticated\` with \`accountState: "full"\`, skip to step 3. If it reports a pending signup, follow its \`next\` command instead of registering again. If it reports \`unauthenticated\` with a non-null \`requestedOrgId\`, this directory is linked to an organization (\`.runtype/project.json\`, \`--org\`, or \`RUNTYPE_ORG\`) that has no stored login: do not register a new account. Have the user privately run \`runtype auth login --api-key <key>\` with a key from that organization (or \`runtype login\` in their own terminal), then verify with \`runtype auth whoami --no-tty\`. If the link looks wrong, confirm with the user before \`runtype unlink\`. If \`RUNTYPE_API_KEY\` is configured, verify it with \`runtype auth whoami --no-tty\` and reuse that account before following stored signup state: \`auth status\` only inspects stored credentials. Never print the key.
|
|
30
|
+
|
|
31
|
+
### 2. Sign the user up from the terminal
|
|
32
|
+
|
|
33
|
+
Ask the user one question: "What email address should I use for your Runtype account?" Then run:
|
|
34
|
+
|
|
35
|
+
\`\`\`bash
|
|
36
|
+
runtype auth register --email <their-email>
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
This creates the account and emails the user a 6-digit code. Ask the user: "Runtype emailed a 6-digit code to <their-email> — what is it?" Then run:
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
runtype auth verify <code>
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
That completes signup. Every command in this flow prints JSON with a \`next\` field — follow it. Notes:
|
|
46
|
+
|
|
47
|
+
- These commands work without a TTY and never open a browser.
|
|
48
|
+
- \`register\` is safe to re-run; it resumes a pending signup instead of creating a duplicate account.
|
|
49
|
+
- 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.
|
|
50
|
+
- If the email already has an account, \`email_already_registered\` means signup cannot claim it. Have the user configure an API key privately in their terminal/environment (\`RUNTYPE_API_KEY\` or \`runtype auth login --api-key <key>\`), then run \`runtype auth whoami --no-tty\` to verify the effective credential. Do not ask them to paste a key into chat or register a replacement account.
|
|
51
|
+
- A wrong code returns \`otp_invalid\` — ask the user to re-check and run \`runtype auth verify\` again. An expired code returns \`otp_expired\` — 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>\`.
|
|
52
|
+
|
|
53
|
+
### 3. Install skills and the MCP connection
|
|
54
|
+
|
|
55
|
+
\`\`\`bash
|
|
56
|
+
runtype install-mcp
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
This detects one coding harness, installs its Runtype skills, and writes user-level hosted MCP configuration. If selection is ambiguous, use \`--agent <id>\` for the current harness. Relay any harness-owned OAuth action to the user; it may open a browser and is separate from browserless signup.
|
|
60
|
+
|
|
61
|
+
### 4. Keep working in this session with the CLI
|
|
62
|
+
|
|
63
|
+
Do not stop just because the new MCP connection is not visible yet. Use CLI commands for useful first-session work:
|
|
64
|
+
|
|
65
|
+
\`\`\`bash
|
|
66
|
+
runtype auth whoami
|
|
67
|
+
runtype --help
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
Continue the user's task, not an unsolicited inventory of their account. Read command help before mutations. \`runtype mcp tools\` lists hosted tools and \`runtype mcp call <tool>\` invokes one without a restart; use this bridge for build instructions, schemas, validation, and setup checks too.
|
|
71
|
+
|
|
72
|
+
### 5. Tell the user to restart or reload the harness
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
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:
|
|
77
|
+
|
|
78
|
+
> 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.
|
|
79
|
+
|
|
80
|
+
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.
|
|
81
|
+
|
|
82
|
+
### 6. Switch to MCP after restart
|
|
83
|
+
|
|
84
|
+
Once the tools appear, use Runtype MCP first. Do not fall back to the CLI just because it worked previously. For a product build, call \`get_build_instructions\` with:
|
|
85
|
+
|
|
86
|
+
\`\`\`json
|
|
87
|
+
{
|
|
88
|
+
"task": "build-product",
|
|
89
|
+
"description": "Describe the user's product goal here"
|
|
90
|
+
}
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
For a bare flow use \`task: "generate-flow"\`; for scoping use \`"explain-capabilities"\`. Read-only account operations do not need build instructions. Fetch only relevant schema topics, validate before creating or updating, and test safely before reporting success.
|
|
94
|
+
|
|
95
|
+
## If the install needs recovery
|
|
96
|
+
|
|
97
|
+
\`runtype install-mcp\` intentionally changes one confidently selected harness, not every agent config it can find. If detection is ambiguous, re-run it with one explicit id:
|
|
98
|
+
|
|
99
|
+
\`\`\`bash
|
|
100
|
+
runtype install-mcp --agent claude
|
|
101
|
+
runtype install-mcp --agent codex
|
|
102
|
+
runtype install-mcp --agent cursor
|
|
103
|
+
runtype install-mcp --agent gemini
|
|
104
|
+
runtype install-mcp --agent opencode
|
|
105
|
+
runtype install-mcp --agent vscode
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
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.
|
|
109
|
+
|
|
110
|
+
## Auth reference
|
|
111
|
+
|
|
112
|
+
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 — the CLI is the supported client:
|
|
113
|
+
|
|
114
|
+
- \`runtype auth register --email <email>\` — create the account and send the code (one step)
|
|
115
|
+
- \`runtype auth claim <email>\` — re-send or change the email for a pending signup
|
|
116
|
+
- \`runtype auth verify <code>\` — finish signup with the 6-digit code
|
|
117
|
+
- \`runtype auth login --api-key <key>\` — privately store an existing credential; not an account-creation command
|
|
118
|
+
- \`runtype auth status\` — inspect the login this directory uses, linked-org state, and pending signup state
|
|
119
|
+
- \`runtype auth accounts\` — list stored logins (one per organization)
|
|
120
|
+
- \`runtype link <orgId>\` — pin the current project to an organization (writes \`.runtype/project.json\`, no secrets)
|
|
121
|
+
- \`runtype auth whoami --no-tty\` — verify the effective credential (environment key first, then stored login)
|
|
122
|
+
|
|
123
|
+
A user-supplied \`RUNTYPE_API_KEY\` environment variable also authenticates API-backed CLI commands without a stored login; \`auth status\` still describes only the stored state. Never print a credential into the transcript or write one into a committed file.
|
|
124
|
+
|
|
125
|
+
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.
|
|
126
|
+
|
|
127
|
+
## After MCP is active
|
|
128
|
+
|
|
129
|
+
- Call \`get_build_instructions\` before building.
|
|
130
|
+
- Use \`get_platform_documentation\` instead of inventing payload shapes.
|
|
131
|
+
- Let the installed Runtype skills route task-specific work: product builds, live account operations, Persona embeds, templates, and SDK/CLI workflows.
|
|
132
|
+
- If the user gives you a \`use.runtype.com/now?...\`, example, or template/FPO link, use the Runtype template skill and MCP tools rather than opening the login-gated dashboard page.
|
|
133
|
+
- Validate before creating/updating. Check product setup, then test with safe inputs and authorized side effects. Report what passed and what remains blocked or untested.
|
|
134
|
+
|
|
135
|
+
Runtype MCP can create and operate agents, flows, products, records, secrets, evals, and surfaces; attach built-in, HTTP, MCP, flow, and subagent tools; and ship chat, API, webhook, messaging, schedule, MCP, and hosted-page experiences.
|
|
136
|
+
|
|
137
|
+
Docs: [docs.runtype.com](https://docs.runtype.com)
|
|
138
|
+
Dashboard: [use.runtype.com](https://use.runtype.com)
|
|
139
|
+
API: [api.runtype.com](https://api.runtype.com)
|
|
140
|
+
`;
|
|
141
|
+
// src/commands/onboard.ts
|
|
142
|
+
const 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`.";
|
|
143
|
+
const AFTER_RESTART_STEPS = [
|
|
144
|
+
"Use the Runtype MCP server first for all Runtype work.",
|
|
145
|
+
"Start with the get_build_instructions tool before creating or changing resources.",
|
|
146
|
+
"Do not fall back to the Runtype CLI once the MCP connection is active."
|
|
147
|
+
];
|
|
148
|
+
const EXISTING_PROJECT_ADVICE = "Building AI features into an existing codebase? After restart, decide with the user whether flow/agent config should be hosted (dashboard-editable, called via the dispatch API) or code-first in this repo via the TypeScript SDK. They run on the same platform and can be mixed.";
|
|
149
|
+
/**
|
|
150
|
+
* Trivial heuristic for "the user ran onboarding inside an existing project":
|
|
151
|
+
* the cwd has a package.json or a .git directory and is not the CLI itself.
|
|
152
|
+
*/
|
|
153
|
+
function detectExistingProject(cwd = process.cwd()) {
|
|
154
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
155
|
+
const hasPackageJson = existsSync(packageJsonPath);
|
|
156
|
+
if (!hasPackageJson && !existsSync(join(cwd, ".git"))) return false;
|
|
157
|
+
if (hasPackageJson) try {
|
|
158
|
+
if (JSON.parse(readFileSync(packageJsonPath, "utf8")).name === "@runtypelabs/cli") return false;
|
|
159
|
+
} catch {}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
const defaultDependencies = {
|
|
163
|
+
getEnvApiKey: () => getRuntimeApiKey(),
|
|
164
|
+
getStoredCredentials: async () => {
|
|
165
|
+
const credentials = await new CredentialStore().getCredentials();
|
|
166
|
+
if (!credentials) return null;
|
|
167
|
+
return {
|
|
168
|
+
userId: credentials.userId,
|
|
169
|
+
orgId: credentials.orgId
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
ensureAuth,
|
|
173
|
+
selectAgent,
|
|
174
|
+
configureMcp: (agent, options) => configureMcpAgent(agent, options),
|
|
175
|
+
authenticateMcp: (agent, options) => authenticateMcpAgent(agent, options),
|
|
176
|
+
installSkills: installSkillsForAgent,
|
|
177
|
+
isExistingProject: detectExistingProject,
|
|
178
|
+
setExitCode: (code) => {
|
|
179
|
+
process.exitCode = code;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
async function resolveAuth(dependencies, options) {
|
|
183
|
+
if (dependencies.getEnvApiKey()) return {
|
|
184
|
+
authenticated: true,
|
|
185
|
+
method: "env"
|
|
186
|
+
};
|
|
187
|
+
const stored = await dependencies.getStoredCredentials();
|
|
188
|
+
if (stored) return {
|
|
189
|
+
authenticated: true,
|
|
190
|
+
method: "stored",
|
|
191
|
+
userId: stored.userId,
|
|
192
|
+
orgId: stored.orgId
|
|
193
|
+
};
|
|
194
|
+
if (!options.interactiveLogin) return {
|
|
195
|
+
authenticated: false,
|
|
196
|
+
instruction: AUTH_LOGIN_INSTRUCTION
|
|
197
|
+
};
|
|
198
|
+
if (!await dependencies.ensureAuth({ exitOnFailure: false })) return {
|
|
199
|
+
authenticated: false,
|
|
200
|
+
instruction: AUTH_LOGIN_INSTRUCTION
|
|
201
|
+
};
|
|
202
|
+
const after = await dependencies.getStoredCredentials();
|
|
203
|
+
return {
|
|
204
|
+
authenticated: true,
|
|
205
|
+
method: "browser-login",
|
|
206
|
+
userId: after?.userId,
|
|
207
|
+
orgId: after?.orgId
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function printAuthLine(auth) {
|
|
211
|
+
if (!auth.authenticated) {
|
|
212
|
+
console.log(chalk.yellow(`! Not signed in. ${auth.instruction}`));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (auth.method === "env") {
|
|
216
|
+
console.log(chalk.green("✓ Signed in with RUNTYPE_API_KEY from the environment."));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const identity = auth.userId ? ` (user ${auth.userId}${auth.orgId ? `, org ${auth.orgId}` : ""})` : "";
|
|
220
|
+
if (auth.method === "stored") console.log(chalk.green(`✓ Already signed in${identity}.`));
|
|
221
|
+
else console.log(chalk.green(`✓ Signed in${identity}.`));
|
|
222
|
+
}
|
|
223
|
+
function createOnboardCommand(overrides = {}) {
|
|
224
|
+
const dependencies = {
|
|
225
|
+
...defaultDependencies,
|
|
226
|
+
...overrides
|
|
227
|
+
};
|
|
228
|
+
return new Command("onboard").description("Set up Runtype for this coding agent: sign in, install skills and OAuth MCP").option("--agent <id>", "Coding agent to configure (auto-detected by default)").option("--force", "Replace a conflicting MCP server named \"runtype\"").option("--dry-run", "Show the selected agent and planned changes without writing them").option("--no-login", "Skip browser sign-in and MCP OAuth").option("--no-skills", "Skip installing Runtype agent skills").option("--json", "Structured JSON output; implies --no-login").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").option("--print-instructions", "Print the canonical agent onboarding markdown and exit").action(async (options, cmd) => {
|
|
229
|
+
if (options.printInstructions) {
|
|
230
|
+
console.log(AGENT_ONBOARDING_MARKDOWN);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const globals = cmd.optsWithGlobals();
|
|
234
|
+
const jsonMode = !!(options.json ?? globals.json);
|
|
235
|
+
const tty = isTTY(options);
|
|
236
|
+
const loginAllowed = !jsonMode && tty && options.login !== false;
|
|
237
|
+
if (!jsonMode) {
|
|
238
|
+
console.log(chalk.cyan("\nRuntype onboarding"));
|
|
239
|
+
console.log("Runtype is a platform for building AI products: agents, flows, evals, and surfaces your team ships to its own users.");
|
|
240
|
+
console.log(chalk.dim("This command signs you in, installs Runtype skills and the hosted OAuth MCP server, then prints next steps.\n"));
|
|
241
|
+
}
|
|
242
|
+
let auth;
|
|
243
|
+
try {
|
|
244
|
+
auth = await resolveAuth(dependencies, { interactiveLogin: loginAllowed && !options.dryRun });
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
247
|
+
if (jsonMode) printJson({
|
|
248
|
+
status: "error",
|
|
249
|
+
code: "AUTH_RESOLUTION_FAILED",
|
|
250
|
+
error: message
|
|
251
|
+
});
|
|
252
|
+
else console.error(chalk.red(`Could not read Runtype credentials: ${message}`));
|
|
253
|
+
dependencies.setExitCode(1);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (!jsonMode) printAuthLine(auth);
|
|
257
|
+
let agent;
|
|
258
|
+
try {
|
|
259
|
+
agent = await dependencies.selectAgent({
|
|
260
|
+
agent: options.agent,
|
|
261
|
+
json: jsonMode
|
|
262
|
+
}, tty);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
265
|
+
if (jsonMode) printJson({
|
|
266
|
+
status: "error",
|
|
267
|
+
code: "AGENT_DETECTION_FAILED",
|
|
268
|
+
auth,
|
|
269
|
+
error: message
|
|
270
|
+
});
|
|
271
|
+
else console.error(chalk.red(message));
|
|
272
|
+
dependencies.setExitCode(1);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const definition = MCP_AGENT_DEFINITIONS[agent];
|
|
276
|
+
if (!jsonMode) console.log(chalk.cyan(`\nSetting up Runtype for ${definition.displayName}…`));
|
|
277
|
+
const configuration = await dependencies.configureMcp(agent, {
|
|
278
|
+
force: options.force,
|
|
279
|
+
dryRun: options.dryRun
|
|
280
|
+
});
|
|
281
|
+
if (!configuration.configured) {
|
|
282
|
+
if (jsonMode) printJson({
|
|
283
|
+
status: "error",
|
|
284
|
+
code: "MCP_CONFIG_FAILED",
|
|
285
|
+
agent,
|
|
286
|
+
auth,
|
|
287
|
+
configuration
|
|
288
|
+
});
|
|
289
|
+
else console.error(chalk.red(configuration.error ?? "Could not configure Runtype MCP."));
|
|
290
|
+
dependencies.setExitCode(1);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!jsonMode) {
|
|
294
|
+
if (options.dryRun) console.log(chalk.green("✓ Would install the user-level Runtype MCP connection."));
|
|
295
|
+
else if (configuration.changed) console.log(chalk.green("✓ Installed the user-level Runtype MCP connection."));
|
|
296
|
+
else console.log(chalk.green("✓ Runtype MCP is already configured at the correct URL."));
|
|
297
|
+
if (configuration.configPath) console.log(chalk.dim(` ${configuration.configPath}`));
|
|
298
|
+
if (configuration.backupPath) console.log(chalk.dim(` Backup: ${configuration.backupPath}`));
|
|
299
|
+
}
|
|
300
|
+
const authentication = await dependencies.authenticateMcp(agent, {
|
|
301
|
+
login: loginAllowed,
|
|
302
|
+
dryRun: options.dryRun
|
|
303
|
+
});
|
|
304
|
+
if (!jsonMode && !options.dryRun) {
|
|
305
|
+
if (authentication.authenticated) console.log(chalk.green("✓ MCP OAuth completed."));
|
|
306
|
+
else if (authentication.error) console.log(chalk.yellow(`! ${authentication.error}`));
|
|
307
|
+
else if (authentication.instruction) console.log(chalk.yellow(`! OAuth action required: ${authentication.instruction}`));
|
|
308
|
+
}
|
|
309
|
+
const skills = options.skills === false || options.dryRun ? {
|
|
310
|
+
status: options.dryRun ? "planned" : "skipped",
|
|
311
|
+
installedAgents: [],
|
|
312
|
+
failedAgents: []
|
|
313
|
+
} : await dependencies.installSkills(agent, jsonMode);
|
|
314
|
+
if (!jsonMode && !options.dryRun && skills.status === "error") console.log(chalk.yellow(`! Runtype MCP is configured, but skill installation failed: ${skills.error ?? "unknown error"}`));
|
|
315
|
+
const restartInstruction = getMcpRestartInstruction(agent);
|
|
316
|
+
const existingProject = dependencies.isExistingProject();
|
|
317
|
+
const nextSteps = [];
|
|
318
|
+
if (options.dryRun) nextSteps.push(`Re-run \`runtype onboard --agent ${agent}\` without --dry-run to sign in, install skills, and configure MCP.`);
|
|
319
|
+
else {
|
|
320
|
+
if (!auth.authenticated && auth.instruction) nextSteps.push(auth.instruction);
|
|
321
|
+
if (authentication.instruction) nextSteps.push(authentication.instruction);
|
|
322
|
+
if (authentication.error && !authentication.instruction) nextSteps.push("Resolve the OAuth blocker reported above, then re-run `runtype onboard`.");
|
|
323
|
+
if (skills.status === "error") nextSteps.push(`Retry skill installation with \`runtype skills install --agent ${getMcpSkillsAgentId(agent)}\`.`);
|
|
324
|
+
else if (skills.status === "skipped") nextSteps.push(`Install the Runtype skills with \`runtype skills install --agent ${getMcpSkillsAgentId(agent)}\`.`);
|
|
325
|
+
nextSteps.push("Continue this first session with the Runtype CLI; this running agent may not see the new MCP connection yet.");
|
|
326
|
+
if (auth.authenticated && authentication.authenticated && skills.status !== "error") nextSteps.push(`Tell the user that Runtype is set up, then ask them to restart or reload ${definition.displayName}.`);
|
|
327
|
+
else nextSteps.push("Tell the user that setup still has the action(s) above; do not call onboarding complete yet.");
|
|
328
|
+
nextSteps.push(`After completing any action above, ${restartInstruction}`);
|
|
329
|
+
}
|
|
330
|
+
const partial = !options.dryRun && (Boolean(authentication.error) || skills.status === "error");
|
|
331
|
+
const status = options.dryRun ? "planned" : partial ? "partial" : auth.authenticated && authentication.authenticated ? "ok" : "action-required";
|
|
332
|
+
if (jsonMode) printJson({
|
|
333
|
+
status,
|
|
334
|
+
agent,
|
|
335
|
+
auth,
|
|
336
|
+
configuration,
|
|
337
|
+
authentication,
|
|
338
|
+
skills,
|
|
339
|
+
activation: options.dryRun ? "not-configured" : "restart-required",
|
|
340
|
+
restartInstruction,
|
|
341
|
+
afterRestart: AFTER_RESTART_STEPS,
|
|
342
|
+
...existingProject ? { existingProjectAdvice: EXISTING_PROJECT_ADVICE } : {},
|
|
343
|
+
nextSteps
|
|
344
|
+
});
|
|
345
|
+
else {
|
|
346
|
+
console.log(chalk.bold(`\nOnboarding status: ${status}`));
|
|
347
|
+
console.log(chalk.bold("\nDo exactly this next:"));
|
|
348
|
+
nextSteps.forEach((step, index) => console.log(` ${index + 1}. ${step}`));
|
|
349
|
+
console.log(chalk.bold(`\nAfter restarting ${definition.displayName}:`));
|
|
350
|
+
AFTER_RESTART_STEPS.forEach((step) => console.log(` - ${step}`));
|
|
351
|
+
if (existingProject) console.log(`\n${EXISTING_PROJECT_ADVICE}`);
|
|
352
|
+
console.log("");
|
|
353
|
+
}
|
|
354
|
+
if (partial) dependencies.setExitCode(1);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
const onboardCommand = createOnboardCommand();
|
|
358
|
+
export { onboardCommand };
|
|
359
|
+
|