@runtypelabs/cli 2.28.1 → 2.30.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/README.md +39 -0
- package/dist/index.js +1383 -185
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -136,15 +136,15 @@ var init_credential_store = __esm({
|
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
// src/index.ts
|
|
139
|
-
import { Command as
|
|
140
|
-
import
|
|
139
|
+
import { Command as Command39 } from "commander";
|
|
140
|
+
import chalk48 from "chalk";
|
|
141
141
|
|
|
142
142
|
// src/lib/load-env.ts
|
|
143
143
|
import { readFileSync } from "fs";
|
|
144
144
|
import { resolve } from "path";
|
|
145
|
-
function loadEnv(
|
|
145
|
+
function loadEnv(path20 = resolve(process.cwd(), ".env")) {
|
|
146
146
|
try {
|
|
147
|
-
const content = readFileSync(
|
|
147
|
+
const content = readFileSync(path20, "utf8").replace(/\r\n?/g, "\n");
|
|
148
148
|
for (const line of content.split("\n")) {
|
|
149
149
|
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
150
150
|
const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/);
|
|
@@ -1386,10 +1386,10 @@ function mergeDefs(...defs) {
|
|
|
1386
1386
|
function cloneDef(schema) {
|
|
1387
1387
|
return mergeDefs(schema._zod.def);
|
|
1388
1388
|
}
|
|
1389
|
-
function getElementAtPath(obj,
|
|
1390
|
-
if (!
|
|
1389
|
+
function getElementAtPath(obj, path20) {
|
|
1390
|
+
if (!path20)
|
|
1391
1391
|
return obj;
|
|
1392
|
-
return
|
|
1392
|
+
return path20.reduce((acc, key) => acc?.[key], obj);
|
|
1393
1393
|
}
|
|
1394
1394
|
function promiseAllObject(promisesObj) {
|
|
1395
1395
|
const keys = Object.keys(promisesObj);
|
|
@@ -1798,11 +1798,11 @@ function explicitlyAborted(x2, startIndex = 0) {
|
|
|
1798
1798
|
}
|
|
1799
1799
|
return false;
|
|
1800
1800
|
}
|
|
1801
|
-
function prefixIssues(
|
|
1801
|
+
function prefixIssues(path20, issues) {
|
|
1802
1802
|
return issues.map((iss) => {
|
|
1803
1803
|
var _a3;
|
|
1804
1804
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1805
|
-
iss.path.unshift(
|
|
1805
|
+
iss.path.unshift(path20);
|
|
1806
1806
|
return iss;
|
|
1807
1807
|
});
|
|
1808
1808
|
}
|
|
@@ -1949,16 +1949,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1949
1949
|
}
|
|
1950
1950
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
1951
1951
|
const fieldErrors = { _errors: [] };
|
|
1952
|
-
const processError = (error52,
|
|
1952
|
+
const processError = (error52, path20 = []) => {
|
|
1953
1953
|
for (const issue2 of error52.issues) {
|
|
1954
1954
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1955
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1955
|
+
issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
|
|
1956
1956
|
} else if (issue2.code === "invalid_key") {
|
|
1957
|
-
processError({ issues: issue2.issues }, [...
|
|
1957
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
1958
1958
|
} else if (issue2.code === "invalid_element") {
|
|
1959
|
-
processError({ issues: issue2.issues }, [...
|
|
1959
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
1960
1960
|
} else {
|
|
1961
|
-
const fullpath = [...
|
|
1961
|
+
const fullpath = [...path20, ...issue2.path];
|
|
1962
1962
|
if (fullpath.length === 0) {
|
|
1963
1963
|
fieldErrors._errors.push(mapper(issue2));
|
|
1964
1964
|
} else {
|
|
@@ -1985,17 +1985,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1985
1985
|
}
|
|
1986
1986
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
1987
1987
|
const result = { errors: [] };
|
|
1988
|
-
const processError = (error52,
|
|
1988
|
+
const processError = (error52, path20 = []) => {
|
|
1989
1989
|
var _a3, _b;
|
|
1990
1990
|
for (const issue2 of error52.issues) {
|
|
1991
1991
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1992
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1992
|
+
issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
|
|
1993
1993
|
} else if (issue2.code === "invalid_key") {
|
|
1994
|
-
processError({ issues: issue2.issues }, [...
|
|
1994
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
1995
1995
|
} else if (issue2.code === "invalid_element") {
|
|
1996
|
-
processError({ issues: issue2.issues }, [...
|
|
1996
|
+
processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
|
|
1997
1997
|
} else {
|
|
1998
|
-
const fullpath = [...
|
|
1998
|
+
const fullpath = [...path20, ...issue2.path];
|
|
1999
1999
|
if (fullpath.length === 0) {
|
|
2000
2000
|
result.errors.push(mapper(issue2));
|
|
2001
2001
|
continue;
|
|
@@ -2027,8 +2027,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2027
2027
|
}
|
|
2028
2028
|
function toDotPath(_path) {
|
|
2029
2029
|
const segs = [];
|
|
2030
|
-
const
|
|
2031
|
-
for (const seg of
|
|
2030
|
+
const path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2031
|
+
for (const seg of path20) {
|
|
2032
2032
|
if (typeof seg === "number")
|
|
2033
2033
|
segs.push(`[${seg}]`);
|
|
2034
2034
|
else if (typeof seg === "symbol")
|
|
@@ -14720,13 +14720,13 @@ function resolveRef(ref, ctx) {
|
|
|
14720
14720
|
if (!ref.startsWith("#")) {
|
|
14721
14721
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14722
14722
|
}
|
|
14723
|
-
const
|
|
14724
|
-
if (
|
|
14723
|
+
const path20 = ref.slice(1).split("/").filter(Boolean);
|
|
14724
|
+
if (path20.length === 0) {
|
|
14725
14725
|
return ctx.rootSchema;
|
|
14726
14726
|
}
|
|
14727
14727
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
14728
|
-
if (
|
|
14729
|
-
const key =
|
|
14728
|
+
if (path20[0] === defsKey) {
|
|
14729
|
+
const key = path20[1];
|
|
14730
14730
|
if (!key || !ctx.defs[key]) {
|
|
14731
14731
|
throw new Error(`Reference not found: ${ref}`);
|
|
14732
14732
|
}
|
|
@@ -15326,8 +15326,8 @@ function renderDefaultModelStrengthsGuide() {
|
|
|
15326
15326
|
}
|
|
15327
15327
|
|
|
15328
15328
|
// ../shared/dist/chunk-UIQ7MVBO.mjs
|
|
15329
|
-
function getNestedValue(obj,
|
|
15330
|
-
let normalizedPath =
|
|
15329
|
+
function getNestedValue(obj, path20) {
|
|
15330
|
+
let normalizedPath = path20;
|
|
15331
15331
|
normalizedPath = normalizedPath.replace(/^\$\.?/, "");
|
|
15332
15332
|
normalizedPath = normalizedPath.replace(/\[(\d+)\]/g, ".$1");
|
|
15333
15333
|
normalizedPath = normalizedPath.replace(/\[['"]([^'"\]]+)['"]\]/g, ".$1");
|
|
@@ -17250,12 +17250,12 @@ function addSemanticIssues(schema, ctx, pathPrefix = []) {
|
|
|
17250
17250
|
}
|
|
17251
17251
|
}
|
|
17252
17252
|
for (const [name, property] of Object.entries(schema.properties)) {
|
|
17253
|
-
const
|
|
17253
|
+
const path20 = [...pathPrefix, "properties", name];
|
|
17254
17254
|
if (property.type === "string" && property.enum) {
|
|
17255
17255
|
if (new Set(property.enum).size !== property.enum.length) {
|
|
17256
17256
|
ctx.addIssue({
|
|
17257
17257
|
code: "custom",
|
|
17258
|
-
path: [...
|
|
17258
|
+
path: [...path20, "enum"],
|
|
17259
17259
|
message: "Enum values must be unique"
|
|
17260
17260
|
});
|
|
17261
17261
|
}
|
|
@@ -17263,15 +17263,15 @@ function addSemanticIssues(schema, ctx, pathPrefix = []) {
|
|
|
17263
17263
|
if (property.type === "number" && property.minimum !== void 0 && property.maximum !== void 0 && property.minimum > property.maximum) {
|
|
17264
17264
|
ctx.addIssue({
|
|
17265
17265
|
code: "custom",
|
|
17266
|
-
path:
|
|
17266
|
+
path: path20,
|
|
17267
17267
|
message: "minimum must be less than or equal to maximum"
|
|
17268
17268
|
});
|
|
17269
17269
|
}
|
|
17270
17270
|
if (property.type === "object") {
|
|
17271
|
-
addSemanticIssues(property, ctx,
|
|
17271
|
+
addSemanticIssues(property, ctx, path20);
|
|
17272
17272
|
}
|
|
17273
17273
|
if (property.type === "array" && property.items.type === "object") {
|
|
17274
|
-
addSemanticIssues(property.items, ctx, [...
|
|
17274
|
+
addSemanticIssues(property.items, ctx, [...path20, "items"]);
|
|
17275
17275
|
}
|
|
17276
17276
|
}
|
|
17277
17277
|
}
|
|
@@ -20577,8 +20577,8 @@ function validateCodeToolNameCollisions(toolNames, stepRef, code, preSeededCount
|
|
|
20577
20577
|
}
|
|
20578
20578
|
return null;
|
|
20579
20579
|
}
|
|
20580
|
-
function formatPath(
|
|
20581
|
-
return
|
|
20580
|
+
function formatPath(path20) {
|
|
20581
|
+
return path20.reduce((acc, part) => {
|
|
20582
20582
|
if (typeof part === "number") {
|
|
20583
20583
|
return `${acc}[${part}]`;
|
|
20584
20584
|
}
|
|
@@ -20699,17 +20699,17 @@ function collectTransformDataCodeToolNameCheck(toolsConfig, stepIndex, stepRef,
|
|
|
20699
20699
|
if (typeof toolId !== "string") {
|
|
20700
20700
|
continue;
|
|
20701
20701
|
}
|
|
20702
|
-
const
|
|
20702
|
+
const path20 = `flowSteps[${stepIndex}].config.tools.toolIds[${toolIdIndex}]`;
|
|
20703
20703
|
const named = parseNamedToolRef(toolId);
|
|
20704
20704
|
if (named) {
|
|
20705
|
-
selectedToolNames.push({ name: named.name, path:
|
|
20705
|
+
selectedToolNames.push({ name: named.name, path: path20 });
|
|
20706
20706
|
continue;
|
|
20707
20707
|
}
|
|
20708
20708
|
const { customToolIds } = separateToolIds([toolId]);
|
|
20709
20709
|
if (customToolIds.length === 0) {
|
|
20710
20710
|
continue;
|
|
20711
20711
|
}
|
|
20712
|
-
selectedToolIds.push({ toolId, path:
|
|
20712
|
+
selectedToolIds.push({ toolId, path: path20 });
|
|
20713
20713
|
}
|
|
20714
20714
|
if (selectedToolIds.length > 0 || selectedToolNames.length > 0) {
|
|
20715
20715
|
pendingChecks.hasAccountScopedReferences = true;
|
|
@@ -20742,10 +20742,10 @@ function collectCodeModeToolNameCheck(toolsConfig, stepIndex, stepRef, pendingCh
|
|
|
20742
20742
|
if (typeof entry !== "string" || entry.length === 0 || entry.endsWith(":*")) {
|
|
20743
20743
|
continue;
|
|
20744
20744
|
}
|
|
20745
|
-
const
|
|
20745
|
+
const path20 = `flowSteps[${stepIndex}].config.tools.codeModeConfig.toolPool[${toolPoolIndex}]`;
|
|
20746
20746
|
const named = parseNamedToolRef(entry);
|
|
20747
20747
|
if (named) {
|
|
20748
|
-
selectedToolNames.push({ name: named.name, path:
|
|
20748
|
+
selectedToolNames.push({ name: named.name, path: path20 });
|
|
20749
20749
|
continue;
|
|
20750
20750
|
}
|
|
20751
20751
|
if (runtimeToolNames.has(entry)) {
|
|
@@ -20756,7 +20756,7 @@ function collectCodeModeToolNameCheck(toolsConfig, stepIndex, stepRef, pendingCh
|
|
|
20756
20756
|
if (customToolIds.length === 0) {
|
|
20757
20757
|
continue;
|
|
20758
20758
|
}
|
|
20759
|
-
selectedToolIds.push({ toolId: entry, path:
|
|
20759
|
+
selectedToolIds.push({ toolId: entry, path: path20 });
|
|
20760
20760
|
}
|
|
20761
20761
|
if (selectedToolIds.length > 0 || selectedToolNames.length > 0) {
|
|
20762
20762
|
pendingChecks.hasAccountScopedReferences = true;
|
|
@@ -20806,19 +20806,19 @@ var TEMPLATE_STRING_FIELDS = /* @__PURE__ */ new Set([
|
|
|
20806
20806
|
"script"
|
|
20807
20807
|
]);
|
|
20808
20808
|
var NON_FLOW_VARIABLE_SUBTREE_KEYS = /* @__PURE__ */ new Set(["tools"]);
|
|
20809
|
-
function extractTemplateStrings(config3,
|
|
20809
|
+
function extractTemplateStrings(config3, path20 = []) {
|
|
20810
20810
|
const results = [];
|
|
20811
20811
|
if (typeof config3 === "string") {
|
|
20812
|
-
const fieldName =
|
|
20813
|
-
const parentFieldName =
|
|
20812
|
+
const fieldName = path20[path20.length - 1];
|
|
20813
|
+
const parentFieldName = path20.length >= 2 ? path20[path20.length - 2] : void 0;
|
|
20814
20814
|
if (fieldName && TEMPLATE_STRING_FIELDS.has(fieldName) || parentFieldName && TEMPLATE_STRING_FIELDS.has(parentFieldName)) {
|
|
20815
|
-
results.push({ value: config3, fieldPath:
|
|
20815
|
+
results.push({ value: config3, fieldPath: path20.join(".") });
|
|
20816
20816
|
}
|
|
20817
20817
|
return results;
|
|
20818
20818
|
}
|
|
20819
20819
|
if (Array.isArray(config3)) {
|
|
20820
20820
|
for (const [index, item] of config3.entries()) {
|
|
20821
|
-
results.push(...extractTemplateStrings(item, [...
|
|
20821
|
+
results.push(...extractTemplateStrings(item, [...path20, String(index)]));
|
|
20822
20822
|
}
|
|
20823
20823
|
return results;
|
|
20824
20824
|
}
|
|
@@ -20826,9 +20826,9 @@ function extractTemplateStrings(config3, path19 = []) {
|
|
|
20826
20826
|
for (const [key, val] of Object.entries(config3)) {
|
|
20827
20827
|
if (NON_FLOW_VARIABLE_SUBTREE_KEYS.has(key)) continue;
|
|
20828
20828
|
if (typeof val === "string" && TEMPLATE_STRING_FIELDS.has(key)) {
|
|
20829
|
-
results.push({ value: val, fieldPath: [...
|
|
20829
|
+
results.push({ value: val, fieldPath: [...path20, key].join(".") });
|
|
20830
20830
|
} else if (typeof val === "object" && val !== null) {
|
|
20831
|
-
results.push(...extractTemplateStrings(val, [...
|
|
20831
|
+
results.push(...extractTemplateStrings(val, [...path20, key]));
|
|
20832
20832
|
}
|
|
20833
20833
|
}
|
|
20834
20834
|
}
|
|
@@ -20859,8 +20859,8 @@ function extractVariableReferences(config3) {
|
|
|
20859
20859
|
while ((match = pattern.exec(value)) !== null) {
|
|
20860
20860
|
const expr = match[1];
|
|
20861
20861
|
if (!expr) continue;
|
|
20862
|
-
for (const
|
|
20863
|
-
references.push({ variable:
|
|
20862
|
+
for (const path20 of extractTemplateExpressionPaths(expr)) {
|
|
20863
|
+
references.push({ variable: path20, fieldPath });
|
|
20864
20864
|
}
|
|
20865
20865
|
}
|
|
20866
20866
|
}
|
|
@@ -21078,7 +21078,7 @@ function validateStoreVectorSource(flowSteps, buckets, declaredFlowInputs) {
|
|
|
21078
21078
|
if (outputVar) declaredVariables.add(outputVar);
|
|
21079
21079
|
}
|
|
21080
21080
|
}
|
|
21081
|
-
function checkConditionExpression(expr,
|
|
21081
|
+
function checkConditionExpression(expr, path20, stepRef, buckets) {
|
|
21082
21082
|
if (typeof expr !== "string" || !expr.includes("{{")) return;
|
|
21083
21083
|
const match = UNQUOTED_TEMPLATE_BEFORE_OP.exec(expr) || UNQUOTED_TEMPLATE_AFTER_OP.exec(expr);
|
|
21084
21084
|
if (!match) return;
|
|
@@ -21088,7 +21088,7 @@ function checkConditionExpression(expr, path19, stepRef, buckets) {
|
|
|
21088
21088
|
{
|
|
21089
21089
|
code: "CONDITION_UNQUOTED_TEMPLATE_COMPARISON",
|
|
21090
21090
|
message: `Expression compares an unquoted {{...}} placeholder against a string literal (\`${snippet}\`). \`condition\` / \`when\` predicates are JavaScript evaluated after template substitution, so a non-numeric value substitutes in as a bare identifier (e.g. \`healthy === 'watch'\`) and throws "ReferenceError: <value> is not defined" at runtime. Quote the placeholder \u2014 e.g. '{{var}}' === '...' \u2014 or compare numerically.`,
|
|
21091
|
-
path:
|
|
21091
|
+
path: path20,
|
|
21092
21092
|
step: stepRef,
|
|
21093
21093
|
details: { snippet }
|
|
21094
21094
|
},
|
|
@@ -21294,24 +21294,24 @@ function collectAccountScopedReferences(step, stepIndex, pendingChecks) {
|
|
|
21294
21294
|
if (step.type === "execute-agent") {
|
|
21295
21295
|
const agentId = step.config.agentId;
|
|
21296
21296
|
if (typeof agentId === "string" && !agentId.includes("{{")) {
|
|
21297
|
-
const
|
|
21298
|
-
registerAgentReference(agentId,
|
|
21297
|
+
const path20 = `flowSteps[${stepIndex}].config.agentId`;
|
|
21298
|
+
registerAgentReference(agentId, path20, stepRef, pendingChecks);
|
|
21299
21299
|
}
|
|
21300
21300
|
return;
|
|
21301
21301
|
}
|
|
21302
21302
|
if (step.type === "tool-call") {
|
|
21303
21303
|
const toolId = step.config.toolId;
|
|
21304
21304
|
if (typeof toolId === "string") {
|
|
21305
|
-
const
|
|
21305
|
+
const path20 = `flowSteps[${stepIndex}].config.toolId`;
|
|
21306
21306
|
const named = parseNamedToolRef(toolId);
|
|
21307
21307
|
if (named) {
|
|
21308
21308
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21309
|
-
registerReference(pendingChecks.toolReferencesByName, named.name, { path:
|
|
21309
|
+
registerReference(pendingChecks.toolReferencesByName, named.name, { path: path20, step: stepRef });
|
|
21310
21310
|
} else {
|
|
21311
21311
|
const { customToolIds } = separateToolIds([toolId]);
|
|
21312
21312
|
if (customToolIds.length > 0) {
|
|
21313
21313
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21314
|
-
registerReference(pendingChecks.toolReferencesById, toolId, { path:
|
|
21314
|
+
registerReference(pendingChecks.toolReferencesById, toolId, { path: path20, step: stepRef });
|
|
21315
21315
|
}
|
|
21316
21316
|
}
|
|
21317
21317
|
}
|
|
@@ -21330,17 +21330,17 @@ function collectAccountScopedReferences(step, stepIndex, pendingChecks) {
|
|
|
21330
21330
|
if (typeof toolId !== "string") {
|
|
21331
21331
|
continue;
|
|
21332
21332
|
}
|
|
21333
|
-
const
|
|
21333
|
+
const path20 = `flowSteps[${stepIndex}].config.tools.toolIds[${toolIdIndex}]`;
|
|
21334
21334
|
const named = parseNamedToolRef(toolId);
|
|
21335
21335
|
if (named) {
|
|
21336
21336
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21337
|
-
registerReference(pendingChecks.toolReferencesByName, named.name, { path:
|
|
21337
|
+
registerReference(pendingChecks.toolReferencesByName, named.name, { path: path20, step: stepRef });
|
|
21338
21338
|
continue;
|
|
21339
21339
|
}
|
|
21340
21340
|
const { customToolIds } = separateToolIds([toolId]);
|
|
21341
21341
|
if (customToolIds.length > 0) {
|
|
21342
21342
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21343
|
-
registerReference(pendingChecks.toolReferencesById, toolId, { path:
|
|
21343
|
+
registerReference(pendingChecks.toolReferencesById, toolId, { path: path20, step: stepRef });
|
|
21344
21344
|
}
|
|
21345
21345
|
}
|
|
21346
21346
|
}
|
|
@@ -21378,22 +21378,22 @@ function collectAccountScopedReferences(step, stepIndex, pendingChecks) {
|
|
|
21378
21378
|
}
|
|
21379
21379
|
}
|
|
21380
21380
|
}
|
|
21381
|
-
function registerAgentReference(agentId,
|
|
21381
|
+
function registerAgentReference(agentId, path20, stepRef, pendingChecks) {
|
|
21382
21382
|
const named = parseNamedAgentRef(agentId);
|
|
21383
21383
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21384
21384
|
if (named) {
|
|
21385
|
-
registerReference(pendingChecks.agentReferencesByName, named.name, { path:
|
|
21385
|
+
registerReference(pendingChecks.agentReferencesByName, named.name, { path: path20, step: stepRef });
|
|
21386
21386
|
} else {
|
|
21387
|
-
registerReference(pendingChecks.agentReferencesById, agentId, { path:
|
|
21387
|
+
registerReference(pendingChecks.agentReferencesById, agentId, { path: path20, step: stepRef });
|
|
21388
21388
|
}
|
|
21389
21389
|
}
|
|
21390
|
-
function registerFlowReference(flowId,
|
|
21390
|
+
function registerFlowReference(flowId, path20, stepRef, pendingChecks) {
|
|
21391
21391
|
const named = parseNamedFlowRef(flowId);
|
|
21392
21392
|
pendingChecks.hasAccountScopedReferences = true;
|
|
21393
21393
|
if (named) {
|
|
21394
|
-
registerReference(pendingChecks.flowReferencesByName, named.name, { path:
|
|
21394
|
+
registerReference(pendingChecks.flowReferencesByName, named.name, { path: path20, step: stepRef });
|
|
21395
21395
|
} else {
|
|
21396
|
-
registerReference(pendingChecks.flowReferencesById, flowId, { path:
|
|
21396
|
+
registerReference(pendingChecks.flowReferencesById, flowId, { path: path20, step: stepRef });
|
|
21397
21397
|
}
|
|
21398
21398
|
}
|
|
21399
21399
|
function validateSubagentConfig(value, stepTools, stepIndex, stepRef) {
|
|
@@ -41354,7 +41354,7 @@ function buildDashboardUrl(opts) {
|
|
|
41354
41354
|
while (base.endsWith("/")) {
|
|
41355
41355
|
base = base.slice(0, -1);
|
|
41356
41356
|
}
|
|
41357
|
-
const
|
|
41357
|
+
const path20 = opts.path.startsWith("/") ? opts.path : `/${opts.path}`;
|
|
41358
41358
|
const params = new URLSearchParams();
|
|
41359
41359
|
if (isValidAccountId(opts.account)) {
|
|
41360
41360
|
params.set(ACCOUNT_QUERY_PARAM, opts.account);
|
|
@@ -41367,7 +41367,7 @@ function buildDashboardUrl(opts) {
|
|
|
41367
41367
|
}
|
|
41368
41368
|
}
|
|
41369
41369
|
const qs = params.toString();
|
|
41370
|
-
return qs ? `${base}${
|
|
41370
|
+
return qs ? `${base}${path20}?${qs}` : `${base}${path20}`;
|
|
41371
41371
|
}
|
|
41372
41372
|
function parseAccountId(value) {
|
|
41373
41373
|
if (!value) return null;
|
|
@@ -41975,6 +41975,14 @@ var mcpToolFulfillmentSchema = external_exports.object({
|
|
|
41975
41975
|
rung: external_exports.literal("mcp_tool"),
|
|
41976
41976
|
tool: external_exports.string(),
|
|
41977
41977
|
args: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
41978
|
+
/**
|
|
41979
|
+
* Operator-supplied arguments that MUST be merged into `args` before the
|
|
41980
|
+
* call does anything — executing `args` alone is accepted by the tool but
|
|
41981
|
+
* writes nothing, so a consumer that sees this field non-empty must obtain
|
|
41982
|
+
* these values (from the operator or a secret store) first. Keyed by arg
|
|
41983
|
+
* name; the value describes what to supply.
|
|
41984
|
+
*/
|
|
41985
|
+
pendingArgs: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
41978
41986
|
summary: external_exports.string()
|
|
41979
41987
|
});
|
|
41980
41988
|
var restFulfillmentSchema = external_exports.object({
|
|
@@ -42050,8 +42058,8 @@ var FLAT_ADVANCED_CONFIG_KEYS = {
|
|
|
42050
42058
|
var FLAT_ADVANCED_CONFIG_KEY_LIST = Object.keys(
|
|
42051
42059
|
FLAT_ADVANCED_CONFIG_KEYS
|
|
42052
42060
|
);
|
|
42053
|
-
function createIssue(severity, code, message,
|
|
42054
|
-
return { code, message, path:
|
|
42061
|
+
function createIssue(severity, code, message, path20, suggestedFix) {
|
|
42062
|
+
return { code, message, path: path20, severity, ...suggestedFix ? { suggestedFix } : {} };
|
|
42055
42063
|
}
|
|
42056
42064
|
function emptyResult() {
|
|
42057
42065
|
return { valid: true, errors: [], warnings: [], recommendations: [] };
|
|
@@ -42886,16 +42894,16 @@ var FLOW_PREFIX = Object.values(RUNTIME_PREFIXES).find((spec) => spec.namespace
|
|
|
42886
42894
|
var SECRET_PREFIX = Object.values(RUNTIME_PREFIXES).find(
|
|
42887
42895
|
(spec) => spec.namespace === "secret"
|
|
42888
42896
|
).prefix;
|
|
42889
|
-
function collectStringLeafPaths(value,
|
|
42897
|
+
function collectStringLeafPaths(value, path20 = []) {
|
|
42890
42898
|
if (typeof value === "string") {
|
|
42891
|
-
return [{ path:
|
|
42899
|
+
return [{ path: path20, value }];
|
|
42892
42900
|
}
|
|
42893
42901
|
if (Array.isArray(value)) {
|
|
42894
|
-
return value.flatMap((item, index) => collectStringLeafPaths(item, [...
|
|
42902
|
+
return value.flatMap((item, index) => collectStringLeafPaths(item, [...path20, String(index)]));
|
|
42895
42903
|
}
|
|
42896
42904
|
if (value && typeof value === "object") {
|
|
42897
42905
|
return Object.entries(value).flatMap(
|
|
42898
|
-
([key, nestedValue]) => collectStringLeafPaths(nestedValue, [...
|
|
42906
|
+
([key, nestedValue]) => collectStringLeafPaths(nestedValue, [...path20, key])
|
|
42899
42907
|
);
|
|
42900
42908
|
}
|
|
42901
42909
|
return [];
|
|
@@ -46656,13 +46664,13 @@ function parseJsonObject(raw, label) {
|
|
|
46656
46664
|
}
|
|
46657
46665
|
return parsed;
|
|
46658
46666
|
}
|
|
46659
|
-
function readJsonFile(
|
|
46667
|
+
function readJsonFile(path20, opts = {}) {
|
|
46660
46668
|
const label = opts.label ?? "file";
|
|
46661
46669
|
let raw;
|
|
46662
46670
|
try {
|
|
46663
|
-
raw = readFileSync3(
|
|
46671
|
+
raw = readFileSync3(path20, "utf-8");
|
|
46664
46672
|
} catch {
|
|
46665
|
-
console.error(chalk2.red(`Failed to read ${label}: ${
|
|
46673
|
+
console.error(chalk2.red(`Failed to read ${label}: ${path20}`));
|
|
46666
46674
|
process.exit(1);
|
|
46667
46675
|
}
|
|
46668
46676
|
const obj = parseJsonObject(raw, label);
|
|
@@ -61023,7 +61031,7 @@ function mergeMarathonSessionSummaries(existingSessions, nextSessions, offset) {
|
|
|
61023
61031
|
}
|
|
61024
61032
|
return Array.from(byIndex.values()).sort((left, right) => left.index - right.index);
|
|
61025
61033
|
}
|
|
61026
|
-
function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status = "complete",
|
|
61034
|
+
function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status = "complete", errorMessage3) {
|
|
61027
61035
|
return {
|
|
61028
61036
|
sessionIndex: sessionSummary.index,
|
|
61029
61037
|
status,
|
|
@@ -61042,7 +61050,7 @@ function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status =
|
|
|
61042
61050
|
...event,
|
|
61043
61051
|
data: structuredClone(event.data)
|
|
61044
61052
|
})),
|
|
61045
|
-
...
|
|
61053
|
+
...errorMessage3 ? { errorMessage: errorMessage3 } : {}
|
|
61046
61054
|
};
|
|
61047
61055
|
}
|
|
61048
61056
|
function mapRunTaskStatusToSnapshotStatus(status, fallback) {
|
|
@@ -61170,9 +61178,9 @@ function findLatestStateFile(stateDir) {
|
|
|
61170
61178
|
const files = fs8.readdirSync(dir).filter((f2) => f2.endsWith(".json"));
|
|
61171
61179
|
for (const file2 of files) {
|
|
61172
61180
|
const fullPath = path9.join(dir, file2);
|
|
61173
|
-
const
|
|
61174
|
-
if (!latest ||
|
|
61175
|
-
latest = { name: file2.replace(".json", ""), filePath: fullPath, mtime:
|
|
61181
|
+
const stat2 = fs8.statSync(fullPath);
|
|
61182
|
+
if (!latest || stat2.mtimeMs > latest.mtime) {
|
|
61183
|
+
latest = { name: file2.replace(".json", ""), filePath: fullPath, mtime: stat2.mtimeMs };
|
|
61176
61184
|
}
|
|
61177
61185
|
}
|
|
61178
61186
|
}
|
|
@@ -61504,8 +61512,8 @@ function safeReadTextFile(filePath) {
|
|
|
61504
61512
|
try {
|
|
61505
61513
|
const normalized = normalizeToolPath(filePath).replace(/\\/g, "/");
|
|
61506
61514
|
if (isSensitivePath(normalized)) return null;
|
|
61507
|
-
const
|
|
61508
|
-
if (!
|
|
61515
|
+
const stat2 = fs9.statSync(filePath);
|
|
61516
|
+
if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES_TO_SCAN) return null;
|
|
61509
61517
|
const buffer = fs9.readFileSync(filePath);
|
|
61510
61518
|
if (buffer.includes(0)) return null;
|
|
61511
61519
|
return buffer.toString("utf-8");
|
|
@@ -61672,14 +61680,14 @@ function resolveToolPath(toolPath, options = {}) {
|
|
|
61672
61680
|
}
|
|
61673
61681
|
}
|
|
61674
61682
|
if (resolved.exists) {
|
|
61675
|
-
const
|
|
61676
|
-
if (options.requireFile && !
|
|
61683
|
+
const stat2 = fs10.statSync(resolved.canonicalPath);
|
|
61684
|
+
if (options.requireFile && !stat2.isFile()) {
|
|
61677
61685
|
return {
|
|
61678
61686
|
ok: false,
|
|
61679
61687
|
error: `Path is not a file: ${requestedPath}`
|
|
61680
61688
|
};
|
|
61681
61689
|
}
|
|
61682
|
-
if (options.requireDirectory && !
|
|
61690
|
+
if (options.requireDirectory && !stat2.isDirectory()) {
|
|
61683
61691
|
return {
|
|
61684
61692
|
ok: false,
|
|
61685
61693
|
error: `Path is not a directory: ${requestedPath}`
|
|
@@ -67092,8 +67100,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
67092
67100
|
const client = createCliClient(apiKey);
|
|
67093
67101
|
if (!isTTY(options) || options.json) {
|
|
67094
67102
|
try {
|
|
67095
|
-
const
|
|
67096
|
-
const data = await client.get(
|
|
67103
|
+
const path20 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
67104
|
+
const data = await client.get(path20);
|
|
67097
67105
|
printJson(data);
|
|
67098
67106
|
} catch (error51) {
|
|
67099
67107
|
const message = error51 instanceof Error ? error51.message : "Unknown error";
|
|
@@ -67110,8 +67118,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
67110
67118
|
useEffect32(() => {
|
|
67111
67119
|
const run2 = async () => {
|
|
67112
67120
|
try {
|
|
67113
|
-
const
|
|
67114
|
-
const data = await client.get(
|
|
67121
|
+
const path20 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
67122
|
+
const data = await client.get(path20);
|
|
67115
67123
|
printJson(data);
|
|
67116
67124
|
setSuccess(true);
|
|
67117
67125
|
setLoading(false);
|
|
@@ -71871,6 +71879,7 @@ function isCI() {
|
|
|
71871
71879
|
var SKILLS_OFFER_SKIP_COMMANDS = /* @__PURE__ */ new Set([
|
|
71872
71880
|
"skills",
|
|
71873
71881
|
"install-mcp",
|
|
71882
|
+
"onboard",
|
|
71874
71883
|
"init",
|
|
71875
71884
|
"login",
|
|
71876
71885
|
"auth"
|
|
@@ -72611,8 +72620,6 @@ function getMcpRestartInstruction(agent) {
|
|
|
72611
72620
|
function getMcpSkillsAgentId(agent) {
|
|
72612
72621
|
return MCP_AGENT_DEFINITIONS[agent].skillsAgentId;
|
|
72613
72622
|
}
|
|
72614
|
-
|
|
72615
|
-
// src/commands/install-mcp.ts
|
|
72616
72623
|
async function selectAgent(options, tty) {
|
|
72617
72624
|
if (options.agent) {
|
|
72618
72625
|
const explicit = normalizeMcpAgent(options.agent);
|
|
@@ -72648,6 +72655,8 @@ async function selectAgent(options, tty) {
|
|
|
72648
72655
|
{ promptLabel: "Agent" }
|
|
72649
72656
|
);
|
|
72650
72657
|
}
|
|
72658
|
+
|
|
72659
|
+
// src/commands/install-mcp.ts
|
|
72651
72660
|
async function installSkillsForAgent(agent, jsonMode) {
|
|
72652
72661
|
const rosieId = getMcpSkillsAgentId(agent);
|
|
72653
72662
|
try {
|
|
@@ -72814,13 +72823,378 @@ Setting up Runtype for ${definition.displayName}\u2026`));
|
|
|
72814
72823
|
if (partial2) process.exitCode = 1;
|
|
72815
72824
|
});
|
|
72816
72825
|
|
|
72817
|
-
// src/commands/
|
|
72818
|
-
import {
|
|
72819
|
-
import { join as join14
|
|
72826
|
+
// src/commands/onboard.ts
|
|
72827
|
+
import { existsSync as existsSync16, readFileSync as readFileSync20 } from "fs";
|
|
72828
|
+
import { join as join14 } from "path";
|
|
72820
72829
|
import { Command as Command34 } from "commander";
|
|
72821
72830
|
import chalk42 from "chalk";
|
|
72831
|
+
|
|
72832
|
+
// ../shared/dist/agent-onboarding-content.mjs
|
|
72833
|
+
var AGENT_ONBOARDING_MARKDOWN = `# Runtype for Coding Agents
|
|
72834
|
+
|
|
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 with browser OAuth. Use the Runtype CLI as a bridge during this first session; do not treat it as the final integration.
|
|
72836
|
+
|
|
72837
|
+
## Golden path \u2014 do exactly this
|
|
72838
|
+
|
|
72839
|
+
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
|
+
|
|
72841
|
+
### 1. Install and authenticate the CLI
|
|
72842
|
+
|
|
72843
|
+
\`\`\`bash
|
|
72844
|
+
npm install -g @runtypelabs/cli
|
|
72845
|
+
runtype auth login
|
|
72846
|
+
\`\`\`
|
|
72847
|
+
|
|
72848
|
+
Complete the browser sign-in when it opens. The CLI makes this first session productive even though a running coding harness often cannot discover a newly installed MCP connection until it restarts.
|
|
72849
|
+
|
|
72850
|
+
### 2. Install skills and the OAuth MCP connection
|
|
72851
|
+
|
|
72852
|
+
\`\`\`bash
|
|
72853
|
+
runtype install-mcp
|
|
72854
|
+
\`\`\`
|
|
72855
|
+
|
|
72856
|
+
This command detects the current coding harness, installs the Runtype agent skills for it, writes the user-level hosted MCP configuration, and starts the harness's own OAuth flow when possible. If it asks which harness to configure, select the harness running this session. If a browser opens, have the user complete OAuth.
|
|
72857
|
+
|
|
72858
|
+
Do not manually create an API key, export the CLI key into MCP, or choose a different setup path merely to avoid a restart. CLI authentication and MCP OAuth are separate on purpose: the CLI is the first-session bridge; client-owned OAuth MCP is the durable connection.
|
|
72859
|
+
|
|
72860
|
+
### 3. Keep working in this session with the CLI
|
|
72861
|
+
|
|
72862
|
+
Do not stop just because the new MCP connection is not visible yet. Use CLI commands for useful first-session work:
|
|
72863
|
+
|
|
72864
|
+
\`\`\`bash
|
|
72865
|
+
runtype auth whoami
|
|
72866
|
+
runtype agents list
|
|
72867
|
+
runtype flows list
|
|
72868
|
+
runtype products list
|
|
72869
|
+
runtype --help
|
|
72870
|
+
\`\`\`
|
|
72871
|
+
|
|
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.
|
|
72873
|
+
|
|
72874
|
+
### 4. Tell the user to restart or reload the harness
|
|
72875
|
+
|
|
72876
|
+
Before finishing this session, read the final status and next steps from \`runtype install-mcp\`. Complete any OAuth action it requests and retry any failed skill installation. Do not call setup complete while the command reports \`action-required\`, \`partial\`, or an error.
|
|
72877
|
+
|
|
72878
|
+
Only after skills and OAuth both succeed, tell the user exactly what changed and ask them to restart or reload the coding harness. Use direct language such as:
|
|
72879
|
+
|
|
72880
|
+
> Runtype skills and the OAuth 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
|
+
|
|
72882
|
+
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
|
+
|
|
72884
|
+
### 5. Switch to MCP after restart
|
|
72885
|
+
|
|
72886
|
+
In the next session, use Runtype MCP first. Do not fall back to the CLI just because it worked previously. Start by calling:
|
|
72887
|
+
|
|
72888
|
+
\`\`\`json
|
|
72889
|
+
{
|
|
72890
|
+
"task": "build-product",
|
|
72891
|
+
"description": "Describe the user's product goal here"
|
|
72892
|
+
}
|
|
72893
|
+
\`\`\`
|
|
72894
|
+
|
|
72895
|
+
with \`get_build_instructions\`. Then use \`get_platform_documentation\` for exact schemas and catalogs, create or update resources with MCP tools, validate them, and test the result with a real run.
|
|
72896
|
+
|
|
72897
|
+
## If the install needs recovery
|
|
72898
|
+
|
|
72899
|
+
\`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:
|
|
72900
|
+
|
|
72901
|
+
\`\`\`bash
|
|
72902
|
+
runtype install-mcp --agent claude
|
|
72903
|
+
runtype install-mcp --agent codex
|
|
72904
|
+
runtype install-mcp --agent cursor
|
|
72905
|
+
runtype install-mcp --agent gemini
|
|
72906
|
+
runtype install-mcp --agent opencode
|
|
72907
|
+
runtype install-mcp --agent vscode
|
|
72908
|
+
\`\`\`
|
|
72909
|
+
|
|
72910
|
+
Use only the line for the current harness. Follow the command's OAuth and 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.
|
|
72911
|
+
|
|
72912
|
+
## Headless exception \u2014 not the normal path
|
|
72913
|
+
|
|
72914
|
+
Only use an API key when browser OAuth is genuinely impossible in the user's environment, not merely inconvenient. Ask the user before switching authentication modes, then follow the canonical headless registration and claim protocol at \`https://runtype.com/auth.md\`. Never print a secret into the transcript or write a bearer token directly into a committed config file.
|
|
72915
|
+
|
|
72916
|
+
The hosted MCP endpoint is \`https://api.runtype.com/v1/mcp/protocol\`. A user-supplied \`RUNTYPE_API_KEY\` can authenticate CLI commands in a headless environment, but return to client-owned OAuth MCP when the harness can support it.
|
|
72917
|
+
|
|
72918
|
+
## After MCP is active
|
|
72919
|
+
|
|
72920
|
+
- Call \`get_build_instructions\` before building.
|
|
72921
|
+
- Use \`get_platform_documentation\` instead of inventing payload shapes.
|
|
72922
|
+
- Let the installed Runtype skills route task-specific work: product builds, live account operations, Persona embeds, templates, and SDK/CLI workflows.
|
|
72923
|
+
- 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.
|
|
72924
|
+
- Validate before shipping and test with a real execution.
|
|
72925
|
+
|
|
72926
|
+
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.
|
|
72927
|
+
|
|
72928
|
+
Docs: [runtype.com](https://runtype.com)
|
|
72929
|
+
Dashboard: [use.runtype.com](https://use.runtype.com)
|
|
72930
|
+
API: [api.runtype.com](https://api.runtype.com)
|
|
72931
|
+
`;
|
|
72932
|
+
|
|
72933
|
+
// src/commands/onboard.ts
|
|
72934
|
+
init_credential_store();
|
|
72935
|
+
var AUTH_LOGIN_INSTRUCTION = "Sign in to Runtype: run `runtype auth login` (or set RUNTYPE_API_KEY), then re-run `runtype onboard`.";
|
|
72936
|
+
var AFTER_RESTART_STEPS = [
|
|
72937
|
+
"Use the Runtype MCP server first for all Runtype work.",
|
|
72938
|
+
"Start with the get_build_instructions tool before creating or changing resources.",
|
|
72939
|
+
"Do not fall back to the Runtype CLI once the MCP connection is active."
|
|
72940
|
+
];
|
|
72941
|
+
var 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.";
|
|
72942
|
+
function detectExistingProject(cwd = process.cwd()) {
|
|
72943
|
+
const packageJsonPath = join14(cwd, "package.json");
|
|
72944
|
+
const hasPackageJson = existsSync16(packageJsonPath);
|
|
72945
|
+
if (!hasPackageJson && !existsSync16(join14(cwd, ".git"))) return false;
|
|
72946
|
+
if (hasPackageJson) {
|
|
72947
|
+
try {
|
|
72948
|
+
const parsed = JSON.parse(readFileSync20(packageJsonPath, "utf8"));
|
|
72949
|
+
if (parsed.name === "@runtypelabs/cli") return false;
|
|
72950
|
+
} catch {
|
|
72951
|
+
}
|
|
72952
|
+
}
|
|
72953
|
+
return true;
|
|
72954
|
+
}
|
|
72955
|
+
var defaultDependencies = {
|
|
72956
|
+
getEnvApiKey: () => getRuntimeApiKey(),
|
|
72957
|
+
getStoredCredentials: async () => {
|
|
72958
|
+
const credentials = await new CredentialStore().getCredentials();
|
|
72959
|
+
if (!credentials) return null;
|
|
72960
|
+
return { userId: credentials.userId, orgId: credentials.orgId };
|
|
72961
|
+
},
|
|
72962
|
+
ensureAuth,
|
|
72963
|
+
selectAgent,
|
|
72964
|
+
configureMcp: (agent, options) => configureMcpAgent(agent, options),
|
|
72965
|
+
authenticateMcp: (agent, options) => authenticateMcpAgent(agent, options),
|
|
72966
|
+
installSkills: installSkillsForAgent,
|
|
72967
|
+
isExistingProject: detectExistingProject,
|
|
72968
|
+
setExitCode: (code) => {
|
|
72969
|
+
process.exitCode = code;
|
|
72970
|
+
}
|
|
72971
|
+
};
|
|
72972
|
+
async function resolveAuth(dependencies, options) {
|
|
72973
|
+
if (dependencies.getEnvApiKey()) {
|
|
72974
|
+
return { authenticated: true, method: "env" };
|
|
72975
|
+
}
|
|
72976
|
+
const stored = await dependencies.getStoredCredentials();
|
|
72977
|
+
if (stored) {
|
|
72978
|
+
return { authenticated: true, method: "stored", userId: stored.userId, orgId: stored.orgId };
|
|
72979
|
+
}
|
|
72980
|
+
if (!options.interactiveLogin) {
|
|
72981
|
+
return { authenticated: false, instruction: AUTH_LOGIN_INSTRUCTION };
|
|
72982
|
+
}
|
|
72983
|
+
const apiKey = await dependencies.ensureAuth({ exitOnFailure: false });
|
|
72984
|
+
if (!apiKey) {
|
|
72985
|
+
return { authenticated: false, instruction: AUTH_LOGIN_INSTRUCTION };
|
|
72986
|
+
}
|
|
72987
|
+
const after = await dependencies.getStoredCredentials();
|
|
72988
|
+
return {
|
|
72989
|
+
authenticated: true,
|
|
72990
|
+
method: "browser-login",
|
|
72991
|
+
userId: after?.userId,
|
|
72992
|
+
orgId: after?.orgId
|
|
72993
|
+
};
|
|
72994
|
+
}
|
|
72995
|
+
function printAuthLine(auth) {
|
|
72996
|
+
if (!auth.authenticated) {
|
|
72997
|
+
console.log(chalk42.yellow(`! Not signed in. ${auth.instruction}`));
|
|
72998
|
+
return;
|
|
72999
|
+
}
|
|
73000
|
+
if (auth.method === "env") {
|
|
73001
|
+
console.log(chalk42.green("\u2713 Signed in with RUNTYPE_API_KEY from the environment."));
|
|
73002
|
+
return;
|
|
73003
|
+
}
|
|
73004
|
+
const identity = auth.userId ? ` (user ${auth.userId}${auth.orgId ? `, org ${auth.orgId}` : ""})` : "";
|
|
73005
|
+
if (auth.method === "stored") {
|
|
73006
|
+
console.log(chalk42.green(`\u2713 Already signed in${identity}.`));
|
|
73007
|
+
} else {
|
|
73008
|
+
console.log(chalk42.green(`\u2713 Signed in${identity}.`));
|
|
73009
|
+
}
|
|
73010
|
+
}
|
|
73011
|
+
function createOnboardCommand(overrides = {}) {
|
|
73012
|
+
const dependencies = { ...defaultDependencies, ...overrides };
|
|
73013
|
+
return new Command34("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) => {
|
|
73014
|
+
if (options.printInstructions) {
|
|
73015
|
+
console.log(AGENT_ONBOARDING_MARKDOWN);
|
|
73016
|
+
return;
|
|
73017
|
+
}
|
|
73018
|
+
const globals = cmd.optsWithGlobals();
|
|
73019
|
+
const jsonMode = !!(options.json ?? globals.json);
|
|
73020
|
+
const tty = isTTY(options);
|
|
73021
|
+
const loginAllowed = !jsonMode && tty && options.login !== false;
|
|
73022
|
+
if (!jsonMode) {
|
|
73023
|
+
console.log(chalk42.cyan("\nRuntype onboarding"));
|
|
73024
|
+
console.log(
|
|
73025
|
+
"Runtype is a platform for building AI products: agents, flows, evals, and surfaces your team ships to its own users."
|
|
73026
|
+
);
|
|
73027
|
+
console.log(
|
|
73028
|
+
chalk42.dim(
|
|
73029
|
+
"This command signs you in, installs Runtype skills and the hosted OAuth MCP server, then prints next steps.\n"
|
|
73030
|
+
)
|
|
73031
|
+
);
|
|
73032
|
+
}
|
|
73033
|
+
let auth;
|
|
73034
|
+
try {
|
|
73035
|
+
auth = await resolveAuth(dependencies, {
|
|
73036
|
+
interactiveLogin: loginAllowed && !options.dryRun
|
|
73037
|
+
});
|
|
73038
|
+
} catch (error51) {
|
|
73039
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
73040
|
+
if (jsonMode) {
|
|
73041
|
+
printJson({ status: "error", code: "AUTH_RESOLUTION_FAILED", error: message });
|
|
73042
|
+
} else {
|
|
73043
|
+
console.error(chalk42.red(`Could not read Runtype credentials: ${message}`));
|
|
73044
|
+
}
|
|
73045
|
+
dependencies.setExitCode(1);
|
|
73046
|
+
return;
|
|
73047
|
+
}
|
|
73048
|
+
if (!jsonMode) printAuthLine(auth);
|
|
73049
|
+
let agent;
|
|
73050
|
+
try {
|
|
73051
|
+
agent = await dependencies.selectAgent({ agent: options.agent, json: jsonMode }, tty);
|
|
73052
|
+
} catch (error51) {
|
|
73053
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
73054
|
+
if (jsonMode) {
|
|
73055
|
+
printJson({ status: "error", code: "AGENT_DETECTION_FAILED", auth, error: message });
|
|
73056
|
+
} else {
|
|
73057
|
+
console.error(chalk42.red(message));
|
|
73058
|
+
}
|
|
73059
|
+
dependencies.setExitCode(1);
|
|
73060
|
+
return;
|
|
73061
|
+
}
|
|
73062
|
+
const definition = MCP_AGENT_DEFINITIONS[agent];
|
|
73063
|
+
if (!jsonMode) {
|
|
73064
|
+
console.log(chalk42.cyan(`
|
|
73065
|
+
Setting up Runtype for ${definition.displayName}\u2026`));
|
|
73066
|
+
}
|
|
73067
|
+
const configuration = await dependencies.configureMcp(agent, {
|
|
73068
|
+
force: options.force,
|
|
73069
|
+
dryRun: options.dryRun
|
|
73070
|
+
});
|
|
73071
|
+
if (!configuration.configured) {
|
|
73072
|
+
if (jsonMode) {
|
|
73073
|
+
printJson({ status: "error", code: "MCP_CONFIG_FAILED", agent, auth, configuration });
|
|
73074
|
+
} else {
|
|
73075
|
+
console.error(chalk42.red(configuration.error ?? "Could not configure Runtype MCP."));
|
|
73076
|
+
}
|
|
73077
|
+
dependencies.setExitCode(1);
|
|
73078
|
+
return;
|
|
73079
|
+
}
|
|
73080
|
+
if (!jsonMode) {
|
|
73081
|
+
if (options.dryRun) {
|
|
73082
|
+
console.log(chalk42.green("\u2713 Would install the user-level Runtype MCP connection."));
|
|
73083
|
+
} else if (configuration.changed) {
|
|
73084
|
+
console.log(chalk42.green("\u2713 Installed the user-level Runtype MCP connection."));
|
|
73085
|
+
} else {
|
|
73086
|
+
console.log(chalk42.green("\u2713 Runtype MCP is already configured at the correct URL."));
|
|
73087
|
+
}
|
|
73088
|
+
if (configuration.configPath) {
|
|
73089
|
+
console.log(chalk42.dim(` ${configuration.configPath}`));
|
|
73090
|
+
}
|
|
73091
|
+
if (configuration.backupPath) {
|
|
73092
|
+
console.log(chalk42.dim(` Backup: ${configuration.backupPath}`));
|
|
73093
|
+
}
|
|
73094
|
+
}
|
|
73095
|
+
const authentication = await dependencies.authenticateMcp(agent, {
|
|
73096
|
+
login: loginAllowed,
|
|
73097
|
+
dryRun: options.dryRun
|
|
73098
|
+
});
|
|
73099
|
+
if (!jsonMode && !options.dryRun) {
|
|
73100
|
+
if (authentication.authenticated) {
|
|
73101
|
+
console.log(chalk42.green("\u2713 MCP OAuth completed."));
|
|
73102
|
+
} else if (authentication.error) {
|
|
73103
|
+
console.log(chalk42.yellow(`! ${authentication.error}`));
|
|
73104
|
+
} else if (authentication.instruction) {
|
|
73105
|
+
console.log(chalk42.yellow(`! OAuth action required: ${authentication.instruction}`));
|
|
73106
|
+
}
|
|
73107
|
+
}
|
|
73108
|
+
const skills = options.skills === false || options.dryRun ? {
|
|
73109
|
+
status: options.dryRun ? "planned" : "skipped",
|
|
73110
|
+
installedAgents: [],
|
|
73111
|
+
failedAgents: []
|
|
73112
|
+
} : await dependencies.installSkills(agent, jsonMode);
|
|
73113
|
+
if (!jsonMode && !options.dryRun && skills.status === "error") {
|
|
73114
|
+
console.log(
|
|
73115
|
+
chalk42.yellow(
|
|
73116
|
+
`! Runtype MCP is configured, but skill installation failed: ${skills.error ?? "unknown error"}`
|
|
73117
|
+
)
|
|
73118
|
+
);
|
|
73119
|
+
}
|
|
73120
|
+
const restartInstruction = getMcpRestartInstruction(agent);
|
|
73121
|
+
const existingProject = dependencies.isExistingProject();
|
|
73122
|
+
const nextSteps = [];
|
|
73123
|
+
if (options.dryRun) {
|
|
73124
|
+
nextSteps.push(
|
|
73125
|
+
`Re-run \`runtype onboard --agent ${agent}\` without --dry-run to sign in, install skills, and configure MCP.`
|
|
73126
|
+
);
|
|
73127
|
+
} else {
|
|
73128
|
+
if (!auth.authenticated && auth.instruction) nextSteps.push(auth.instruction);
|
|
73129
|
+
if (authentication.instruction) nextSteps.push(authentication.instruction);
|
|
73130
|
+
if (authentication.error && !authentication.instruction) {
|
|
73131
|
+
nextSteps.push("Resolve the OAuth blocker reported above, then re-run `runtype onboard`.");
|
|
73132
|
+
}
|
|
73133
|
+
if (skills.status === "error") {
|
|
73134
|
+
nextSteps.push(
|
|
73135
|
+
`Retry skill installation with \`runtype skills install --agent ${getMcpSkillsAgentId(agent)}\`.`
|
|
73136
|
+
);
|
|
73137
|
+
} else if (skills.status === "skipped") {
|
|
73138
|
+
nextSteps.push(
|
|
73139
|
+
`Install the Runtype skills with \`runtype skills install --agent ${getMcpSkillsAgentId(agent)}\`.`
|
|
73140
|
+
);
|
|
73141
|
+
}
|
|
73142
|
+
nextSteps.push(
|
|
73143
|
+
"Continue this first session with the Runtype CLI; this running agent may not see the new MCP connection yet."
|
|
73144
|
+
);
|
|
73145
|
+
if (auth.authenticated && authentication.authenticated && skills.status !== "error") {
|
|
73146
|
+
nextSteps.push(
|
|
73147
|
+
`Tell the user that Runtype is set up, then ask them to restart or reload ${definition.displayName}.`
|
|
73148
|
+
);
|
|
73149
|
+
} else {
|
|
73150
|
+
nextSteps.push(
|
|
73151
|
+
"Tell the user that setup still has the action(s) above; do not call onboarding complete yet."
|
|
73152
|
+
);
|
|
73153
|
+
}
|
|
73154
|
+
nextSteps.push(`After completing any action above, ${restartInstruction}`);
|
|
73155
|
+
}
|
|
73156
|
+
const partial2 = !options.dryRun && (Boolean(authentication.error) || skills.status === "error");
|
|
73157
|
+
const status = options.dryRun ? "planned" : partial2 ? "partial" : auth.authenticated && authentication.authenticated ? "ok" : "action-required";
|
|
73158
|
+
if (jsonMode) {
|
|
73159
|
+
printJson({
|
|
73160
|
+
status,
|
|
73161
|
+
agent,
|
|
73162
|
+
auth,
|
|
73163
|
+
configuration,
|
|
73164
|
+
authentication,
|
|
73165
|
+
skills,
|
|
73166
|
+
activation: options.dryRun ? "not-configured" : "restart-required",
|
|
73167
|
+
restartInstruction,
|
|
73168
|
+
afterRestart: AFTER_RESTART_STEPS,
|
|
73169
|
+
...existingProject ? { existingProjectAdvice: EXISTING_PROJECT_ADVICE } : {},
|
|
73170
|
+
nextSteps
|
|
73171
|
+
});
|
|
73172
|
+
} else {
|
|
73173
|
+
console.log(chalk42.bold(`
|
|
73174
|
+
Onboarding status: ${status}`));
|
|
73175
|
+
console.log(chalk42.bold("\nDo exactly this next:"));
|
|
73176
|
+
nextSteps.forEach((step, index) => console.log(` ${index + 1}. ${step}`));
|
|
73177
|
+
console.log(chalk42.bold(`
|
|
73178
|
+
After restarting ${definition.displayName}:`));
|
|
73179
|
+
AFTER_RESTART_STEPS.forEach((step) => console.log(` - ${step}`));
|
|
73180
|
+
if (existingProject) {
|
|
73181
|
+
console.log(`
|
|
73182
|
+
${EXISTING_PROJECT_ADVICE}`);
|
|
73183
|
+
}
|
|
73184
|
+
console.log("");
|
|
73185
|
+
}
|
|
73186
|
+
if (partial2) dependencies.setExitCode(1);
|
|
73187
|
+
});
|
|
73188
|
+
}
|
|
73189
|
+
var onboardCommand = createOnboardCommand();
|
|
73190
|
+
|
|
73191
|
+
// src/commands/apps.ts
|
|
73192
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync21, lstatSync, statSync as statSync7, existsSync as existsSync17 } from "fs";
|
|
73193
|
+
import { join as join15, relative as relative7 } from "path";
|
|
73194
|
+
import { Command as Command35 } from "commander";
|
|
73195
|
+
import chalk43 from "chalk";
|
|
72822
73196
|
import { zipSync } from "fflate";
|
|
72823
|
-
var appsCommand = new
|
|
73197
|
+
var appsCommand = new Command35("apps").description(
|
|
72824
73198
|
"Manage Runtype Apps (hosted AI apps on *.runtype.run)"
|
|
72825
73199
|
);
|
|
72826
73200
|
var MANIFEST_FILENAME = "runtype.app.json";
|
|
@@ -72829,14 +73203,14 @@ function collectBundleFiles(dir) {
|
|
|
72829
73203
|
const walk = (current) => {
|
|
72830
73204
|
for (const entry of readdirSync7(current)) {
|
|
72831
73205
|
if (entry.startsWith(".")) continue;
|
|
72832
|
-
const full =
|
|
72833
|
-
const
|
|
72834
|
-
if (
|
|
72835
|
-
if (
|
|
73206
|
+
const full = join15(current, entry);
|
|
73207
|
+
const stat2 = lstatSync(full);
|
|
73208
|
+
if (stat2.isSymbolicLink()) continue;
|
|
73209
|
+
if (stat2.isDirectory()) {
|
|
72836
73210
|
if (entry === "node_modules") continue;
|
|
72837
73211
|
walk(full);
|
|
72838
73212
|
} else {
|
|
72839
|
-
zippable[relative7(dir, full).split("\\").join("/")] =
|
|
73213
|
+
zippable[relative7(dir, full).split("\\").join("/")] = readFileSync21(full);
|
|
72840
73214
|
}
|
|
72841
73215
|
}
|
|
72842
73216
|
};
|
|
@@ -72847,7 +73221,7 @@ function fail(message, jsonMode, code) {
|
|
|
72847
73221
|
if (jsonMode) {
|
|
72848
73222
|
printJson({ status: "error", error: message, code });
|
|
72849
73223
|
} else {
|
|
72850
|
-
console.error(
|
|
73224
|
+
console.error(chalk43.red(message));
|
|
72851
73225
|
}
|
|
72852
73226
|
process.exit(1);
|
|
72853
73227
|
}
|
|
@@ -72872,13 +73246,13 @@ appsCommand.command("list").description("List your apps").option("--json", "Stru
|
|
|
72872
73246
|
return;
|
|
72873
73247
|
}
|
|
72874
73248
|
if (data.length === 0) {
|
|
72875
|
-
console.log(
|
|
73249
|
+
console.log(chalk43.yellow("No apps yet. Create one with `runtype apps create`."));
|
|
72876
73250
|
return;
|
|
72877
73251
|
}
|
|
72878
73252
|
for (const app of data) {
|
|
72879
|
-
const status = app.status === "suspended" ?
|
|
72880
|
-
const serving = app.activeVersionId ? "" :
|
|
72881
|
-
console.log(`${
|
|
73253
|
+
const status = app.status === "suspended" ? chalk43.red("suspended") : chalk43.green("active");
|
|
73254
|
+
const serving = app.activeVersionId ? "" : chalk43.dim(" (no active version)");
|
|
73255
|
+
console.log(`${chalk43.bold(app.name)} ${chalk43.dim(app.id)}`);
|
|
72882
73256
|
console.log(` ${app.url} ${status} ${app.visibility}${serving}`);
|
|
72883
73257
|
}
|
|
72884
73258
|
} catch (error51) {
|
|
@@ -72907,9 +73281,9 @@ appsCommand.command("create").description("Create an app (the hostname is {slug}
|
|
|
72907
73281
|
printJson(app);
|
|
72908
73282
|
return;
|
|
72909
73283
|
}
|
|
72910
|
-
console.log(
|
|
73284
|
+
console.log(chalk43.green(`Created ${app.name} (${app.id})`));
|
|
72911
73285
|
console.log(` ${app.url}`);
|
|
72912
|
-
console.log(
|
|
73286
|
+
console.log(chalk43.dim(" Deploy a bundle with `runtype apps deploy <dir> --app " + app.id + "`"));
|
|
72913
73287
|
} catch (error51) {
|
|
72914
73288
|
fail(
|
|
72915
73289
|
`Failed to create app: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
@@ -72925,7 +73299,7 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
72925
73299
|
const jsonMode = !!(options.json ?? globals.json);
|
|
72926
73300
|
const apiKey = await ensureAuth();
|
|
72927
73301
|
if (!apiKey) return;
|
|
72928
|
-
if (!
|
|
73302
|
+
if (!existsSync17(dir) || !statSync7(dir).isDirectory()) {
|
|
72929
73303
|
fail(`Not a directory: ${dir}`, jsonMode, "INVALID_DIR");
|
|
72930
73304
|
}
|
|
72931
73305
|
let bundleFiles = {};
|
|
@@ -72955,7 +73329,7 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
72955
73329
|
const client = createCliClient(apiKey);
|
|
72956
73330
|
try {
|
|
72957
73331
|
const zipBytes = zipSync(bundleFiles);
|
|
72958
|
-
if (!jsonMode) console.log(
|
|
73332
|
+
if (!jsonMode) console.log(chalk43.dim(`Uploading ${(zipBytes.byteLength / 1024).toFixed(1)} KB bundle...`));
|
|
72959
73333
|
const version2 = await appsApi.uploadVersion(client, options.app, zipBytes);
|
|
72960
73334
|
let app;
|
|
72961
73335
|
if (options.activate) {
|
|
@@ -72973,8 +73347,8 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
72973
73347
|
activated: false
|
|
72974
73348
|
});
|
|
72975
73349
|
} else {
|
|
72976
|
-
console.error(
|
|
72977
|
-
console.error(
|
|
73350
|
+
console.error(chalk43.red(`Uploaded version ${version2.versionNumber}, but activation failed: ${message}`));
|
|
73351
|
+
console.error(chalk43.dim(` Retry with \`runtype apps activate ${options.app} ${version2.id}\` (no re-upload needed).`));
|
|
72978
73352
|
}
|
|
72979
73353
|
process.exit(1);
|
|
72980
73354
|
}
|
|
@@ -72984,14 +73358,14 @@ appsCommand.command("deploy <dir>").description("Zip a bundle directory, upload
|
|
|
72984
73358
|
return;
|
|
72985
73359
|
}
|
|
72986
73360
|
console.log(
|
|
72987
|
-
|
|
73361
|
+
chalk43.green(
|
|
72988
73362
|
`Deployed version ${version2.versionNumber} (${version2.fileCount} files, ${(version2.sizeBytes / 1024).toFixed(1)} KB)`
|
|
72989
73363
|
)
|
|
72990
73364
|
);
|
|
72991
73365
|
if (app) {
|
|
72992
|
-
console.log(` Live at ${
|
|
73366
|
+
console.log(` Live at ${chalk43.bold(app.url)}`);
|
|
72993
73367
|
} else {
|
|
72994
|
-
console.log(
|
|
73368
|
+
console.log(chalk43.dim(` Uploaded but not activated. Activate with \`runtype apps activate ${options.app} ${version2.id}\``));
|
|
72995
73369
|
}
|
|
72996
73370
|
} catch (error51) {
|
|
72997
73371
|
fail(
|
|
@@ -73015,13 +73389,13 @@ appsCommand.command("versions <appId>").description("List an app's versions, new
|
|
|
73015
73389
|
return;
|
|
73016
73390
|
}
|
|
73017
73391
|
if (data.length === 0) {
|
|
73018
|
-
console.log(
|
|
73392
|
+
console.log(chalk43.yellow("No versions uploaded yet."));
|
|
73019
73393
|
return;
|
|
73020
73394
|
}
|
|
73021
73395
|
for (const version2 of data) {
|
|
73022
|
-
const marker = version2.status === "active" ?
|
|
73396
|
+
const marker = version2.status === "active" ? chalk43.green(" (active)") : "";
|
|
73023
73397
|
console.log(
|
|
73024
|
-
`v${version2.versionNumber} ${
|
|
73398
|
+
`v${version2.versionNumber} ${chalk43.dim(version2.id)} ${version2.fileCount} files, ${(version2.sizeBytes / 1024).toFixed(1)} KB${marker}`
|
|
73025
73399
|
);
|
|
73026
73400
|
}
|
|
73027
73401
|
} catch (error51) {
|
|
@@ -73044,8 +73418,8 @@ appsCommand.command("activate <appId> <versionId>").description("Activate an upl
|
|
|
73044
73418
|
printJson({ app, version: version2 });
|
|
73045
73419
|
return;
|
|
73046
73420
|
}
|
|
73047
|
-
console.log(
|
|
73048
|
-
console.log(` Live at ${
|
|
73421
|
+
console.log(chalk43.green(`Activated version ${version2.versionNumber}`));
|
|
73422
|
+
console.log(` Live at ${chalk43.bold(app.url)}`);
|
|
73049
73423
|
} catch (error51) {
|
|
73050
73424
|
fail(
|
|
73051
73425
|
`Activation failed: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
@@ -73066,7 +73440,7 @@ appsCommand.command("delete <appId>").description("Permanently delete an app, it
|
|
|
73066
73440
|
printJson({ status: "deleted", appId });
|
|
73067
73441
|
return;
|
|
73068
73442
|
}
|
|
73069
|
-
console.log(
|
|
73443
|
+
console.log(chalk43.green(`Deleted ${appId}`));
|
|
73070
73444
|
} catch (error51) {
|
|
73071
73445
|
fail(
|
|
73072
73446
|
`Delete failed: ${error51 instanceof Error ? error51.message : String(error51)}`,
|
|
@@ -73077,17 +73451,17 @@ appsCommand.command("delete <appId>").description("Permanently delete an app, it
|
|
|
73077
73451
|
});
|
|
73078
73452
|
|
|
73079
73453
|
// src/commands/update.ts
|
|
73080
|
-
import
|
|
73081
|
-
import { Command as
|
|
73454
|
+
import chalk45 from "chalk";
|
|
73455
|
+
import { Command as Command36 } from "commander";
|
|
73082
73456
|
|
|
73083
73457
|
// src/lib/auto-update.ts
|
|
73084
73458
|
import { spawn as spawn2 } from "child_process";
|
|
73085
73459
|
import {
|
|
73086
73460
|
closeSync,
|
|
73087
|
-
existsSync as
|
|
73461
|
+
existsSync as existsSync18,
|
|
73088
73462
|
mkdirSync as mkdirSync12,
|
|
73089
73463
|
openSync,
|
|
73090
|
-
readFileSync as
|
|
73464
|
+
readFileSync as readFileSync22,
|
|
73091
73465
|
readdirSync as readdirSync8,
|
|
73092
73466
|
realpathSync as realpathSync3,
|
|
73093
73467
|
renameSync,
|
|
@@ -73096,10 +73470,10 @@ import {
|
|
|
73096
73470
|
writeFileSync as writeFileSync10
|
|
73097
73471
|
} from "fs";
|
|
73098
73472
|
import { constants as osConstants } from "os";
|
|
73099
|
-
import { dirname as dirname10, isAbsolute as isAbsolute4, join as
|
|
73473
|
+
import { dirname as dirname10, isAbsolute as isAbsolute4, join as join16, relative as relative8, resolve as resolve12, sep as sep6 } from "path";
|
|
73100
73474
|
|
|
73101
73475
|
// src/lib/update-check.ts
|
|
73102
|
-
import
|
|
73476
|
+
import chalk44 from "chalk";
|
|
73103
73477
|
import Conf3 from "conf";
|
|
73104
73478
|
var UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
73105
73479
|
var UPDATE_NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -73193,7 +73567,7 @@ function notifyFromCachedCliUpdate(args, options = {}) {
|
|
|
73193
73567
|
console.error(message);
|
|
73194
73568
|
});
|
|
73195
73569
|
notify(
|
|
73196
|
-
`${
|
|
73570
|
+
`${chalk44.yellow("Update available:")} ${chalk44.red(currentVersion)} ${chalk44.gray("->")} ${chalk44.green(latestVersion)} ${chalk44.gray(`(${getUpgradeCommand()})`)}`
|
|
73197
73571
|
);
|
|
73198
73572
|
store.set("lastNotifiedAt", now.toISOString());
|
|
73199
73573
|
store.set("lastNotifiedVersion", latestVersion);
|
|
@@ -73252,32 +73626,32 @@ function isStableCliVersion(version2) {
|
|
|
73252
73626
|
return Boolean(version2 && /^\d+\.\d+\.\d+$/.test(version2));
|
|
73253
73627
|
}
|
|
73254
73628
|
function getManagedCliPaths(homeDir = getRuntypeHomeDir()) {
|
|
73255
|
-
const root =
|
|
73629
|
+
const root = join16(homeDir, "cli");
|
|
73256
73630
|
return {
|
|
73257
73631
|
home: homeDir,
|
|
73258
73632
|
root,
|
|
73259
|
-
versions:
|
|
73260
|
-
state:
|
|
73261
|
-
lock:
|
|
73262
|
-
log:
|
|
73633
|
+
versions: join16(root, "versions"),
|
|
73634
|
+
state: join16(root, "current.json"),
|
|
73635
|
+
lock: join16(root, "update.lock"),
|
|
73636
|
+
log: join16(root, "update.log")
|
|
73263
73637
|
};
|
|
73264
73638
|
}
|
|
73265
73639
|
function getVersionRoot(paths, version2) {
|
|
73266
73640
|
if (!isStableCliVersion(version2)) {
|
|
73267
73641
|
throw new Error(`Invalid CLI version: ${version2}`);
|
|
73268
73642
|
}
|
|
73269
|
-
return
|
|
73643
|
+
return join16(paths.versions, version2);
|
|
73270
73644
|
}
|
|
73271
73645
|
function getVersionPackageRoot(paths, version2) {
|
|
73272
|
-
return
|
|
73646
|
+
return join16(getVersionRoot(paths, version2), "node_modules", "@runtypelabs", "cli");
|
|
73273
73647
|
}
|
|
73274
73648
|
function getVersionEntrypoint(paths, version2) {
|
|
73275
|
-
return
|
|
73649
|
+
return join16(getVersionPackageRoot(paths, version2), "dist", "index.js");
|
|
73276
73650
|
}
|
|
73277
73651
|
function getManagedCliState(homeDir = getRuntypeHomeDir()) {
|
|
73278
73652
|
const { state } = getManagedCliPaths(homeDir);
|
|
73279
73653
|
try {
|
|
73280
|
-
const parsed = JSON.parse(
|
|
73654
|
+
const parsed = JSON.parse(readFileSync22(state, "utf8"));
|
|
73281
73655
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
73282
73656
|
} catch {
|
|
73283
73657
|
return {};
|
|
@@ -73296,7 +73670,7 @@ function writeManagedCliState(paths, state) {
|
|
|
73296
73670
|
}
|
|
73297
73671
|
function readInstalledPackageVersion(packageRoot) {
|
|
73298
73672
|
try {
|
|
73299
|
-
const pkg = JSON.parse(
|
|
73673
|
+
const pkg = JSON.parse(readFileSync22(join16(packageRoot, "package.json"), "utf8"));
|
|
73300
73674
|
if (pkg.name === CLI_PACKAGE_NAME && isStableCliVersion(pkg.version)) {
|
|
73301
73675
|
return pkg.version;
|
|
73302
73676
|
}
|
|
@@ -73307,14 +73681,14 @@ function readInstalledPackageVersion(packageRoot) {
|
|
|
73307
73681
|
function isHealthyManagedVersion(paths, version2) {
|
|
73308
73682
|
if (!isStableCliVersion(version2)) return false;
|
|
73309
73683
|
const packageRoot = getVersionPackageRoot(paths, version2);
|
|
73310
|
-
return readInstalledPackageVersion(packageRoot) === version2 &&
|
|
73684
|
+
return readInstalledPackageVersion(packageRoot) === version2 && existsSync18(getVersionEntrypoint(paths, version2));
|
|
73311
73685
|
}
|
|
73312
73686
|
function isPathInside(parent, child) {
|
|
73313
|
-
const canonicalize = (
|
|
73687
|
+
const canonicalize = (path20) => {
|
|
73314
73688
|
try {
|
|
73315
|
-
return realpathSync3(
|
|
73689
|
+
return realpathSync3(path20);
|
|
73316
73690
|
} catch {
|
|
73317
|
-
return resolve12(
|
|
73691
|
+
return resolve12(path20);
|
|
73318
73692
|
}
|
|
73319
73693
|
};
|
|
73320
73694
|
const relativePath = relative8(canonicalize(parent), canonicalize(child));
|
|
@@ -73323,7 +73697,7 @@ function isPathInside(parent, child) {
|
|
|
73323
73697
|
function isSourceCheckout(entrypoint) {
|
|
73324
73698
|
let current = dirname10(entrypoint);
|
|
73325
73699
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
73326
|
-
if (
|
|
73700
|
+
if (existsSync18(join16(current, ".git")) || existsSync18(join16(current, "pnpm-workspace.yaml"))) {
|
|
73327
73701
|
return true;
|
|
73328
73702
|
}
|
|
73329
73703
|
const parent = dirname10(current);
|
|
@@ -73335,7 +73709,7 @@ function isSourceCheckout(entrypoint) {
|
|
|
73335
73709
|
function findProjectRoot(cwd) {
|
|
73336
73710
|
let current = resolve12(cwd);
|
|
73337
73711
|
while (true) {
|
|
73338
|
-
if (
|
|
73712
|
+
if (existsSync18(join16(current, "package.json")) || existsSync18(join16(current, "pnpm-workspace.yaml"))) {
|
|
73339
73713
|
return current;
|
|
73340
73714
|
}
|
|
73341
73715
|
const parent = dirname10(current);
|
|
@@ -73353,7 +73727,7 @@ function shouldManageInstalledCli(context = {}) {
|
|
|
73353
73727
|
const env = context.env ?? process.env;
|
|
73354
73728
|
const entrypoint = context.entrypoint ?? process.argv[1];
|
|
73355
73729
|
const cwd = context.cwd ?? process.cwd();
|
|
73356
|
-
if (!entrypoint || !
|
|
73730
|
+
if (!entrypoint || !existsSync18(entrypoint)) return false;
|
|
73357
73731
|
if (env.NODE_ENV === "test" || Boolean(env.CI)) return false;
|
|
73358
73732
|
if (env.RUNTYPE_DISABLE_AUTO_UPDATE === "true") return false;
|
|
73359
73733
|
if (isEphemeralPackageRun(entrypoint, env)) return false;
|
|
@@ -73553,7 +73927,7 @@ async function installAndActivateCliVersion(version2, options = {}) {
|
|
|
73553
73927
|
if (!releaseLock) return "busy";
|
|
73554
73928
|
const commandRunner = options.commandRunner ?? defaultCommandRunner;
|
|
73555
73929
|
const finalRoot = getVersionRoot(paths, version2);
|
|
73556
|
-
const temporaryRoot =
|
|
73930
|
+
const temporaryRoot = join16(paths.versions, `.${version2}-${process.pid}-${now.getTime()}`);
|
|
73557
73931
|
let installed = false;
|
|
73558
73932
|
try {
|
|
73559
73933
|
mkdirSync12(paths.versions, { recursive: true });
|
|
@@ -73583,9 +73957,9 @@ async function installAndActivateCliVersion(version2, options = {}) {
|
|
|
73583
73957
|
if (installResult.status !== 0) {
|
|
73584
73958
|
throw installResult.error ?? new Error(`npm install exited with ${installResult.status}`);
|
|
73585
73959
|
}
|
|
73586
|
-
const temporaryPackageRoot =
|
|
73587
|
-
const temporaryEntrypoint =
|
|
73588
|
-
if (readInstalledPackageVersion(temporaryPackageRoot) !== version2 || !
|
|
73960
|
+
const temporaryPackageRoot = join16(temporaryRoot, "node_modules", "@runtypelabs", "cli");
|
|
73961
|
+
const temporaryEntrypoint = join16(temporaryPackageRoot, "dist", "index.js");
|
|
73962
|
+
if (readInstalledPackageVersion(temporaryPackageRoot) !== version2 || !existsSync18(temporaryEntrypoint)) {
|
|
73589
73963
|
throw new Error(
|
|
73590
73964
|
`Installed package did not contain a healthy ${CLI_PACKAGE_NAME}@${version2}`
|
|
73591
73965
|
);
|
|
@@ -73610,7 +73984,7 @@ async function installAndActivateCliVersion(version2, options = {}) {
|
|
|
73610
73984
|
`Installed CLI verification failed (expected ${version2}, got ${verifyResult.stdout.trim() || "no version"})`
|
|
73611
73985
|
);
|
|
73612
73986
|
}
|
|
73613
|
-
if (
|
|
73987
|
+
if (existsSync18(finalRoot)) {
|
|
73614
73988
|
const corruptRoot = `${finalRoot}.replaced-${now.getTime()}`;
|
|
73615
73989
|
renameSync(finalRoot, corruptRoot);
|
|
73616
73990
|
try {
|
|
@@ -73657,7 +74031,7 @@ function isAutoUpdateWorkerInvocation(args, env = process.env) {
|
|
|
73657
74031
|
function spawnAutoUpdateWorker(version2, context = {}) {
|
|
73658
74032
|
if (!isStableCliVersion(version2)) return false;
|
|
73659
74033
|
const entrypoint = context.entrypoint ?? process.argv[1];
|
|
73660
|
-
if (!entrypoint || !
|
|
74034
|
+
if (!entrypoint || !existsSync18(entrypoint)) return false;
|
|
73661
74035
|
const paths = getManagedCliPaths();
|
|
73662
74036
|
mkdirSync12(paths.root, { recursive: true });
|
|
73663
74037
|
const logDescriptor = openSync(paths.log, "a", 384);
|
|
@@ -73749,9 +74123,9 @@ function rollbackManagedCli(homeDir = getRuntypeHomeDir()) {
|
|
|
73749
74123
|
}
|
|
73750
74124
|
|
|
73751
74125
|
// src/commands/update.ts
|
|
73752
|
-
var updateCommand = new
|
|
74126
|
+
var updateCommand = new Command36("update").description("Check for, install, or roll back CLI updates").option("--check", "Check for an update without installing it").option("--rollback", "Roll back the most recent managed update").action(async (options) => {
|
|
73753
74127
|
if (options.check && options.rollback) {
|
|
73754
|
-
console.error(
|
|
74128
|
+
console.error(chalk45.red("Use either --check or --rollback, not both"));
|
|
73755
74129
|
process.exitCode = 1;
|
|
73756
74130
|
return;
|
|
73757
74131
|
}
|
|
@@ -73759,10 +74133,10 @@ var updateCommand = new Command35("update").description("Check for, install, or
|
|
|
73759
74133
|
try {
|
|
73760
74134
|
const result = rollbackManagedCli();
|
|
73761
74135
|
const target = result.to ?? "the package-manager-installed version";
|
|
73762
|
-
console.log(
|
|
73763
|
-
console.log(
|
|
74136
|
+
console.log(chalk45.green(`Rolled back ${result.from} -> ${target}`));
|
|
74137
|
+
console.log(chalk45.gray("The rollback will be used on the next invocation."));
|
|
73764
74138
|
} catch (error51) {
|
|
73765
|
-
console.error(
|
|
74139
|
+
console.error(chalk45.red(error51 instanceof Error ? error51.message : String(error51)));
|
|
73766
74140
|
process.exitCode = 1;
|
|
73767
74141
|
}
|
|
73768
74142
|
return;
|
|
@@ -73770,19 +74144,19 @@ var updateCommand = new Command35("update").description("Check for, install, or
|
|
|
73770
74144
|
const currentVersion = getCliVersion();
|
|
73771
74145
|
const latestVersion = await refreshCliUpdateMetadata({ force: true });
|
|
73772
74146
|
if (!latestVersion) {
|
|
73773
|
-
console.error(
|
|
74147
|
+
console.error(chalk45.red("Unable to determine the latest CLI version from npm"));
|
|
73774
74148
|
process.exitCode = 1;
|
|
73775
74149
|
return;
|
|
73776
74150
|
}
|
|
73777
74151
|
const comparison = compareVersions(latestVersion, currentVersion);
|
|
73778
74152
|
if (comparison <= 0) {
|
|
73779
74153
|
const status = comparison === 0 ? "up to date" : "newer than the latest published version";
|
|
73780
|
-
console.log(
|
|
74154
|
+
console.log(chalk45.green(`Runtype CLI ${currentVersion} is ${status}.`));
|
|
73781
74155
|
return;
|
|
73782
74156
|
}
|
|
73783
74157
|
if (options.check) {
|
|
73784
74158
|
console.log(
|
|
73785
|
-
`${
|
|
74159
|
+
`${chalk45.yellow("Update available:")} ${chalk45.red(currentVersion)} ${chalk45.gray("->")} ${chalk45.green(latestVersion)}`
|
|
73786
74160
|
);
|
|
73787
74161
|
return;
|
|
73788
74162
|
}
|
|
@@ -73790,22 +74164,22 @@ var updateCommand = new Command35("update").description("Check for, install, or
|
|
|
73790
74164
|
try {
|
|
73791
74165
|
const result = await installAndActivateCliVersion(latestVersion);
|
|
73792
74166
|
if (result === "busy") {
|
|
73793
|
-
console.log(
|
|
74167
|
+
console.log(chalk45.yellow("Another CLI update is already in progress."));
|
|
73794
74168
|
return;
|
|
73795
74169
|
}
|
|
73796
|
-
console.log(
|
|
73797
|
-
console.log(
|
|
74170
|
+
console.log(chalk45.green(`Runtype CLI ${latestVersion} is ready.`));
|
|
74171
|
+
console.log(chalk45.gray("It will be used on the next invocation."));
|
|
73798
74172
|
} catch (error51) {
|
|
73799
74173
|
console.error(
|
|
73800
|
-
|
|
74174
|
+
chalk45.red(`CLI update failed: ${error51 instanceof Error ? error51.message : error51}`)
|
|
73801
74175
|
);
|
|
73802
74176
|
process.exitCode = 1;
|
|
73803
74177
|
}
|
|
73804
74178
|
});
|
|
73805
74179
|
|
|
73806
74180
|
// src/commands/mcp.ts
|
|
73807
|
-
import { Command as
|
|
73808
|
-
import
|
|
74181
|
+
import { Command as Command37, InvalidArgumentError } from "commander";
|
|
74182
|
+
import chalk46 from "chalk";
|
|
73809
74183
|
|
|
73810
74184
|
// src/lib/mcp-client.ts
|
|
73811
74185
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -74012,9 +74386,9 @@ async function resolveMcpArguments(options, dependencies = {}) {
|
|
|
74012
74386
|
for (const typedField of typedFields) {
|
|
74013
74387
|
const [key, rawValue] = splitField(typedField, "-F");
|
|
74014
74388
|
if (rawValue.startsWith("@")) {
|
|
74015
|
-
const
|
|
74016
|
-
if (!
|
|
74017
|
-
const fileValue =
|
|
74389
|
+
const path20 = rawValue.slice(1);
|
|
74390
|
+
if (!path20) throw new Error("-F file references require @<path> or @-");
|
|
74391
|
+
const fileValue = path20 === "-" ? await deps.readStdin() : await deps.readFile(path20, "utf8");
|
|
74018
74392
|
setField(key, parseTypedValue(fileValue));
|
|
74019
74393
|
} else {
|
|
74020
74394
|
setField(key, parseTypedValue(rawValue));
|
|
@@ -74024,7 +74398,7 @@ async function resolveMcpArguments(options, dependencies = {}) {
|
|
|
74024
74398
|
}
|
|
74025
74399
|
|
|
74026
74400
|
// src/commands/mcp.ts
|
|
74027
|
-
var
|
|
74401
|
+
var defaultDependencies2 = {
|
|
74028
74402
|
ensureAuth,
|
|
74029
74403
|
connect: connectMcpClient,
|
|
74030
74404
|
resolveArguments: resolveMcpArguments,
|
|
@@ -74073,16 +74447,16 @@ async function withMcpClient(options, dependencies, operation, authOptions) {
|
|
|
74073
74447
|
}
|
|
74074
74448
|
function printTools(tools, target) {
|
|
74075
74449
|
if (tools.length === 0) {
|
|
74076
|
-
console.log(
|
|
74450
|
+
console.log(chalk46.gray("No MCP tools found"));
|
|
74077
74451
|
return;
|
|
74078
74452
|
}
|
|
74079
74453
|
const label = target === "code" ? "Code Mode MCP Tools" : "MCP Tools";
|
|
74080
|
-
console.log(
|
|
74454
|
+
console.log(chalk46.cyan(`${label}:`));
|
|
74081
74455
|
for (const tool of tools) {
|
|
74082
74456
|
const description = tool.description?.split("\n", 1)[0];
|
|
74083
|
-
console.log(` ${
|
|
74457
|
+
console.log(` ${chalk46.green(tool.name)}${description ? chalk46.gray(` \u2014 ${description}`) : ""}`);
|
|
74084
74458
|
}
|
|
74085
|
-
console.log(
|
|
74459
|
+
console.log(chalk46.dim(`
|
|
74086
74460
|
Total: ${tools.length} tools`));
|
|
74087
74461
|
}
|
|
74088
74462
|
function printTool(tool) {
|
|
@@ -74112,8 +74486,8 @@ function printCallResult(result, json2) {
|
|
|
74112
74486
|
printJson(result);
|
|
74113
74487
|
}
|
|
74114
74488
|
function createMcpCommand(overrides = {}) {
|
|
74115
|
-
const dependencies = { ...
|
|
74116
|
-
const command = new
|
|
74489
|
+
const dependencies = { ...defaultDependencies2, ...overrides };
|
|
74490
|
+
const command = new Command37("mcp").description(
|
|
74117
74491
|
"Discover and call tools on the Runtype MCP servers"
|
|
74118
74492
|
);
|
|
74119
74493
|
addCommonOptions(
|
|
@@ -74126,7 +74500,7 @@ function createMcpCommand(overrides = {}) {
|
|
|
74126
74500
|
else printTools(tools, options.server);
|
|
74127
74501
|
});
|
|
74128
74502
|
} catch (error51) {
|
|
74129
|
-
console.error(
|
|
74503
|
+
console.error(chalk46.red(`Failed to list MCP tools: ${errorMessage(error51)}`));
|
|
74130
74504
|
dependencies.setExitCode(1);
|
|
74131
74505
|
}
|
|
74132
74506
|
});
|
|
@@ -74144,7 +74518,7 @@ function createMcpCommand(overrides = {}) {
|
|
|
74144
74518
|
else printTool(tool);
|
|
74145
74519
|
});
|
|
74146
74520
|
} catch (error51) {
|
|
74147
|
-
console.error(
|
|
74521
|
+
console.error(chalk46.red(`Failed to inspect MCP tool: ${errorMessage(error51)}`));
|
|
74148
74522
|
dependencies.setExitCode(1);
|
|
74149
74523
|
}
|
|
74150
74524
|
});
|
|
@@ -74182,7 +74556,7 @@ function createMcpCommand(overrides = {}) {
|
|
|
74182
74556
|
usesMcpStdin(options) ? { interactive: false, exitOnFailure: false } : void 0
|
|
74183
74557
|
);
|
|
74184
74558
|
} catch (error51) {
|
|
74185
|
-
console.error(
|
|
74559
|
+
console.error(chalk46.red(`MCP tool call failed: ${errorMessage(error51)}`));
|
|
74186
74560
|
dependencies.setExitCode(1);
|
|
74187
74561
|
}
|
|
74188
74562
|
});
|
|
@@ -74190,6 +74564,828 @@ function createMcpCommand(overrides = {}) {
|
|
|
74190
74564
|
}
|
|
74191
74565
|
var mcpCommand = createMcpCommand();
|
|
74192
74566
|
|
|
74567
|
+
// src/commands/devices.ts
|
|
74568
|
+
import { spawn as spawn4 } from "child_process";
|
|
74569
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
74570
|
+
import { Command as Command38 } from "commander";
|
|
74571
|
+
import chalk47 from "chalk";
|
|
74572
|
+
|
|
74573
|
+
// src/lib/device-provider.ts
|
|
74574
|
+
var DEVICE_SIDECAR_PROTOCOL_VERSION = 1;
|
|
74575
|
+
var DeviceProviderError = class extends Error {
|
|
74576
|
+
constructor(message, code, retryable = false, options) {
|
|
74577
|
+
super(message, options);
|
|
74578
|
+
this.code = code;
|
|
74579
|
+
this.retryable = retryable;
|
|
74580
|
+
this.name = "DeviceProviderError";
|
|
74581
|
+
}
|
|
74582
|
+
code;
|
|
74583
|
+
retryable;
|
|
74584
|
+
};
|
|
74585
|
+
|
|
74586
|
+
// src/lib/macos-device-provider.ts
|
|
74587
|
+
import { spawn as spawn3 } from "child_process";
|
|
74588
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
74589
|
+
import { constants as fsConstants } from "fs";
|
|
74590
|
+
import { access, chmod, copyFile, mkdir, realpath, rename, rm, stat } from "fs/promises";
|
|
74591
|
+
import os6 from "os";
|
|
74592
|
+
import path19 from "path";
|
|
74593
|
+
var MAX_SIDECAR_OUTPUT_BYTES = 1048576;
|
|
74594
|
+
var MAX_HELPER_BYTES = 100 * 1048576;
|
|
74595
|
+
var DEFAULT_SIDECAR_TIMEOUT_MS = 3e4;
|
|
74596
|
+
function isRecord3(value) {
|
|
74597
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74598
|
+
}
|
|
74599
|
+
function parseHello(value) {
|
|
74600
|
+
if (!isRecord3(value) || typeof value.providerId !== "string" || typeof value.protocolVersion !== "number" || typeof value.helperVersion !== "string" || typeof value.platform !== "string" || !Array.isArray(value.capabilities) || !value.capabilities.every((capability) => typeof capability === "string")) {
|
|
74601
|
+
throw new DeviceProviderError(
|
|
74602
|
+
"The native helper returned an invalid hello response",
|
|
74603
|
+
"INVALID_RESPONSE"
|
|
74604
|
+
);
|
|
74605
|
+
}
|
|
74606
|
+
return value;
|
|
74607
|
+
}
|
|
74608
|
+
async function isExecutable(filePath) {
|
|
74609
|
+
try {
|
|
74610
|
+
await access(filePath, fsConstants.X_OK);
|
|
74611
|
+
const details = await stat(filePath);
|
|
74612
|
+
return details.isFile();
|
|
74613
|
+
} catch {
|
|
74614
|
+
return false;
|
|
74615
|
+
}
|
|
74616
|
+
}
|
|
74617
|
+
var MacOSDeviceProvider = class {
|
|
74618
|
+
id = "com.runtype.device.macos";
|
|
74619
|
+
platform;
|
|
74620
|
+
architecture;
|
|
74621
|
+
operatingSystemRelease;
|
|
74622
|
+
homeDirectory;
|
|
74623
|
+
workingDirectory;
|
|
74624
|
+
environment;
|
|
74625
|
+
sidecarTimeoutMs;
|
|
74626
|
+
constructor(options = {}) {
|
|
74627
|
+
this.platform = options.platform ?? process.platform;
|
|
74628
|
+
this.architecture = options.architecture ?? process.arch;
|
|
74629
|
+
this.operatingSystemRelease = options.operatingSystemRelease ?? os6.release();
|
|
74630
|
+
this.homeDirectory = options.homeDirectory ?? os6.homedir();
|
|
74631
|
+
this.workingDirectory = options.workingDirectory ?? process.cwd();
|
|
74632
|
+
this.environment = options.environment ?? process.env;
|
|
74633
|
+
this.sidecarTimeoutMs = options.sidecarTimeoutMs ?? DEFAULT_SIDECAR_TIMEOUT_MS;
|
|
74634
|
+
}
|
|
74635
|
+
async probe() {
|
|
74636
|
+
const unsupportedReason = this.unsupportedReason();
|
|
74637
|
+
if (unsupportedReason) {
|
|
74638
|
+
return {
|
|
74639
|
+
providerId: this.id,
|
|
74640
|
+
supported: false,
|
|
74641
|
+
installed: false,
|
|
74642
|
+
reason: unsupportedReason
|
|
74643
|
+
};
|
|
74644
|
+
}
|
|
74645
|
+
const binaryPath = await this.resolveBinary();
|
|
74646
|
+
if (!binaryPath) {
|
|
74647
|
+
return {
|
|
74648
|
+
providerId: this.id,
|
|
74649
|
+
supported: true,
|
|
74650
|
+
installed: false,
|
|
74651
|
+
reason: "The Runtype macOS device helper is not installed"
|
|
74652
|
+
};
|
|
74653
|
+
}
|
|
74654
|
+
try {
|
|
74655
|
+
const hello = parseHello(await this.invokeAt(binaryPath, "hello"));
|
|
74656
|
+
this.assertCompatible(hello);
|
|
74657
|
+
return {
|
|
74658
|
+
providerId: this.id,
|
|
74659
|
+
supported: true,
|
|
74660
|
+
installed: true,
|
|
74661
|
+
binaryPath,
|
|
74662
|
+
helperVersion: hello.helperVersion,
|
|
74663
|
+
protocolVersion: hello.protocolVersion
|
|
74664
|
+
};
|
|
74665
|
+
} catch (error51) {
|
|
74666
|
+
return {
|
|
74667
|
+
providerId: this.id,
|
|
74668
|
+
supported: true,
|
|
74669
|
+
installed: true,
|
|
74670
|
+
binaryPath,
|
|
74671
|
+
reason: error51 instanceof Error ? error51.message : String(error51)
|
|
74672
|
+
};
|
|
74673
|
+
}
|
|
74674
|
+
}
|
|
74675
|
+
async ensureInstalled(options = {}) {
|
|
74676
|
+
this.assertPlatform();
|
|
74677
|
+
if (!options.helperPath) {
|
|
74678
|
+
const installedPath = await this.resolveInstalledBinary();
|
|
74679
|
+
if (installedPath) {
|
|
74680
|
+
return this.describeInstallation(installedPath, false);
|
|
74681
|
+
}
|
|
74682
|
+
}
|
|
74683
|
+
const sourcePath = options.helperPath ? path19.resolve(options.helperPath) : await this.resolveDevelopmentSource();
|
|
74684
|
+
if (!sourcePath) {
|
|
74685
|
+
throw new DeviceProviderError(
|
|
74686
|
+
[
|
|
74687
|
+
"The Runtype macOS device helper is not installed.",
|
|
74688
|
+
"For this internal-alpha build, compile it from the core repository and pass:",
|
|
74689
|
+
" runtype devices setup --helper-path apps/device-daemon-macos/.build/release/runtype-device"
|
|
74690
|
+
].join("\n"),
|
|
74691
|
+
"HELPER_NOT_INSTALLED"
|
|
74692
|
+
);
|
|
74693
|
+
}
|
|
74694
|
+
const resolvedSource = await realpath(sourcePath).catch(() => sourcePath);
|
|
74695
|
+
const sourceDetails = await stat(resolvedSource).catch(() => null);
|
|
74696
|
+
if (!sourceDetails?.isFile() || sourceDetails.size > MAX_HELPER_BYTES) {
|
|
74697
|
+
throw new DeviceProviderError(
|
|
74698
|
+
`Device helper is not a valid executable file: ${sourcePath}`,
|
|
74699
|
+
"INVALID_HELPER"
|
|
74700
|
+
);
|
|
74701
|
+
}
|
|
74702
|
+
await access(resolvedSource, fsConstants.X_OK).catch((error51) => {
|
|
74703
|
+
throw new DeviceProviderError(
|
|
74704
|
+
`Device helper is not executable: ${sourcePath}`,
|
|
74705
|
+
"INVALID_HELPER",
|
|
74706
|
+
false,
|
|
74707
|
+
{ cause: error51 }
|
|
74708
|
+
);
|
|
74709
|
+
});
|
|
74710
|
+
const sourceHello = parseHello(await this.invokeAt(resolvedSource, "hello"));
|
|
74711
|
+
this.assertCompatible(sourceHello);
|
|
74712
|
+
const targetPath = this.stableBinaryPath;
|
|
74713
|
+
const resolvedTarget = await realpath(targetPath).catch(() => targetPath);
|
|
74714
|
+
if (resolvedSource !== resolvedTarget) {
|
|
74715
|
+
const targetDirectory = path19.dirname(targetPath);
|
|
74716
|
+
await mkdir(targetDirectory, { recursive: true, mode: 448 });
|
|
74717
|
+
await chmod(targetDirectory, 448);
|
|
74718
|
+
const temporaryPath = path19.join(targetDirectory, `.runtype-device-${randomUUID2()}.tmp`);
|
|
74719
|
+
try {
|
|
74720
|
+
await copyFile(resolvedSource, temporaryPath, fsConstants.COPYFILE_EXCL);
|
|
74721
|
+
await chmod(temporaryPath, 448);
|
|
74722
|
+
await rename(temporaryPath, targetPath);
|
|
74723
|
+
} finally {
|
|
74724
|
+
await rm(temporaryPath, { force: true });
|
|
74725
|
+
}
|
|
74726
|
+
}
|
|
74727
|
+
return this.describeInstallation(targetPath, true);
|
|
74728
|
+
}
|
|
74729
|
+
async invoke(method, params = {}) {
|
|
74730
|
+
this.assertPlatform();
|
|
74731
|
+
const binaryPath = await this.resolveBinary();
|
|
74732
|
+
if (!binaryPath) {
|
|
74733
|
+
throw new DeviceProviderError(
|
|
74734
|
+
"The Runtype macOS device helper is not installed. Run `runtype devices setup` first.",
|
|
74735
|
+
"HELPER_NOT_INSTALLED"
|
|
74736
|
+
);
|
|
74737
|
+
}
|
|
74738
|
+
const result = await this.invokeAt(binaryPath, method, params);
|
|
74739
|
+
if (!isRecord3(result)) {
|
|
74740
|
+
throw new DeviceProviderError(
|
|
74741
|
+
"The native helper returned a non-object result",
|
|
74742
|
+
"INVALID_RESPONSE"
|
|
74743
|
+
);
|
|
74744
|
+
}
|
|
74745
|
+
return result;
|
|
74746
|
+
}
|
|
74747
|
+
async removeInstalledBinary() {
|
|
74748
|
+
const installed = await isExecutable(this.stableBinaryPath);
|
|
74749
|
+
if (!installed) return false;
|
|
74750
|
+
await rm(this.stableBinaryPath, { force: true });
|
|
74751
|
+
return true;
|
|
74752
|
+
}
|
|
74753
|
+
get stableBinaryPath() {
|
|
74754
|
+
return path19.join(this.homeDirectory, ".runtype", "device", "bin", "runtype-device");
|
|
74755
|
+
}
|
|
74756
|
+
installedCandidates() {
|
|
74757
|
+
return [
|
|
74758
|
+
this.stableBinaryPath,
|
|
74759
|
+
path19.join(
|
|
74760
|
+
this.homeDirectory,
|
|
74761
|
+
"Applications",
|
|
74762
|
+
"Runtype Device.app",
|
|
74763
|
+
"Contents",
|
|
74764
|
+
"MacOS",
|
|
74765
|
+
"runtype-device"
|
|
74766
|
+
),
|
|
74767
|
+
"/Applications/Runtype Device.app/Contents/MacOS/runtype-device"
|
|
74768
|
+
];
|
|
74769
|
+
}
|
|
74770
|
+
developmentCandidates() {
|
|
74771
|
+
return [
|
|
74772
|
+
...this.environment.RUNTYPE_DEVICE_HELPER_PATH ? [path19.resolve(this.environment.RUNTYPE_DEVICE_HELPER_PATH)] : [],
|
|
74773
|
+
path19.join(
|
|
74774
|
+
this.workingDirectory,
|
|
74775
|
+
"apps",
|
|
74776
|
+
"device-daemon-macos",
|
|
74777
|
+
".build",
|
|
74778
|
+
"release",
|
|
74779
|
+
"runtype-device"
|
|
74780
|
+
),
|
|
74781
|
+
path19.join(
|
|
74782
|
+
this.workingDirectory,
|
|
74783
|
+
"apps",
|
|
74784
|
+
"device-daemon-macos",
|
|
74785
|
+
".build",
|
|
74786
|
+
"debug",
|
|
74787
|
+
"runtype-device"
|
|
74788
|
+
)
|
|
74789
|
+
];
|
|
74790
|
+
}
|
|
74791
|
+
async firstExecutable(candidates) {
|
|
74792
|
+
for (const candidate of candidates) {
|
|
74793
|
+
if (await isExecutable(candidate)) return candidate;
|
|
74794
|
+
}
|
|
74795
|
+
return null;
|
|
74796
|
+
}
|
|
74797
|
+
resolveInstalledBinary() {
|
|
74798
|
+
return this.firstExecutable(this.installedCandidates());
|
|
74799
|
+
}
|
|
74800
|
+
resolveDevelopmentSource() {
|
|
74801
|
+
return this.firstExecutable(this.developmentCandidates());
|
|
74802
|
+
}
|
|
74803
|
+
async resolveBinary() {
|
|
74804
|
+
return await this.resolveInstalledBinary() ?? await this.firstExecutable(this.developmentCandidates());
|
|
74805
|
+
}
|
|
74806
|
+
assertPlatform() {
|
|
74807
|
+
const reason = this.unsupportedReason();
|
|
74808
|
+
if (!reason) return;
|
|
74809
|
+
throw new DeviceProviderError(
|
|
74810
|
+
reason,
|
|
74811
|
+
this.platform === "darwin" ? "UNSUPPORTED_OS_VERSION" : "UNSUPPORTED_PLATFORM"
|
|
74812
|
+
);
|
|
74813
|
+
}
|
|
74814
|
+
unsupportedReason() {
|
|
74815
|
+
if (this.platform !== "darwin") {
|
|
74816
|
+
return `Runtype Device currently supports macOS only (detected ${this.platform}/${this.architecture})`;
|
|
74817
|
+
}
|
|
74818
|
+
const darwinMajor = Number.parseInt(this.operatingSystemRelease.split(".")[0] ?? "", 10);
|
|
74819
|
+
if (!Number.isInteger(darwinMajor) || darwinMajor < 23) {
|
|
74820
|
+
return `Runtype Device requires macOS 14 or newer (detected Darwin ${this.operatingSystemRelease})`;
|
|
74821
|
+
}
|
|
74822
|
+
return null;
|
|
74823
|
+
}
|
|
74824
|
+
assertCompatible(hello) {
|
|
74825
|
+
if (hello.providerId !== this.id || hello.platform !== "macos") {
|
|
74826
|
+
throw new DeviceProviderError(
|
|
74827
|
+
`Unexpected native provider identity: ${hello.providerId}/${hello.platform}`,
|
|
74828
|
+
"PROVIDER_MISMATCH"
|
|
74829
|
+
);
|
|
74830
|
+
}
|
|
74831
|
+
if (hello.protocolVersion !== DEVICE_SIDECAR_PROTOCOL_VERSION) {
|
|
74832
|
+
throw new DeviceProviderError(
|
|
74833
|
+
`Native helper protocol ${hello.protocolVersion} is incompatible with CLI protocol ${DEVICE_SIDECAR_PROTOCOL_VERSION}`,
|
|
74834
|
+
"PROTOCOL_MISMATCH"
|
|
74835
|
+
);
|
|
74836
|
+
}
|
|
74837
|
+
}
|
|
74838
|
+
async describeInstallation(binaryPath, installed) {
|
|
74839
|
+
const hello = parseHello(await this.invokeAt(binaryPath, "hello"));
|
|
74840
|
+
this.assertCompatible(hello);
|
|
74841
|
+
return {
|
|
74842
|
+
providerId: this.id,
|
|
74843
|
+
binaryPath,
|
|
74844
|
+
helperVersion: hello.helperVersion,
|
|
74845
|
+
protocolVersion: hello.protocolVersion,
|
|
74846
|
+
installed
|
|
74847
|
+
};
|
|
74848
|
+
}
|
|
74849
|
+
async invokeAt(binaryPath, method, params = {}) {
|
|
74850
|
+
const id = randomUUID2();
|
|
74851
|
+
const request = JSON.stringify({
|
|
74852
|
+
protocolVersion: DEVICE_SIDECAR_PROTOCOL_VERSION,
|
|
74853
|
+
id,
|
|
74854
|
+
method,
|
|
74855
|
+
params
|
|
74856
|
+
});
|
|
74857
|
+
const child = spawn3(binaryPath, ["control"], {
|
|
74858
|
+
env: this.environment,
|
|
74859
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
74860
|
+
shell: false
|
|
74861
|
+
});
|
|
74862
|
+
const stdout = [];
|
|
74863
|
+
const stderr = [];
|
|
74864
|
+
let stdoutBytes = 0;
|
|
74865
|
+
let stderrBytes = 0;
|
|
74866
|
+
let outputExceeded = false;
|
|
74867
|
+
let timedOut = false;
|
|
74868
|
+
child.stdout.on("data", (chunk) => {
|
|
74869
|
+
stdoutBytes += chunk.length;
|
|
74870
|
+
if (stdoutBytes > MAX_SIDECAR_OUTPUT_BYTES) {
|
|
74871
|
+
outputExceeded = true;
|
|
74872
|
+
child.kill("SIGKILL");
|
|
74873
|
+
return;
|
|
74874
|
+
}
|
|
74875
|
+
stdout.push(chunk);
|
|
74876
|
+
});
|
|
74877
|
+
child.stderr.on("data", (chunk) => {
|
|
74878
|
+
if (stderrBytes >= MAX_SIDECAR_OUTPUT_BYTES) return;
|
|
74879
|
+
stderrBytes += chunk.length;
|
|
74880
|
+
stderr.push(chunk.subarray(0, MAX_SIDECAR_OUTPUT_BYTES - (stderrBytes - chunk.length)));
|
|
74881
|
+
});
|
|
74882
|
+
const completion = new Promise(
|
|
74883
|
+
(resolve13, reject) => {
|
|
74884
|
+
child.once("error", reject);
|
|
74885
|
+
child.once("close", (code, signal) => resolve13({ code, signal }));
|
|
74886
|
+
}
|
|
74887
|
+
);
|
|
74888
|
+
const timeout = setTimeout(() => {
|
|
74889
|
+
timedOut = true;
|
|
74890
|
+
child.kill("SIGKILL");
|
|
74891
|
+
}, this.sidecarTimeoutMs);
|
|
74892
|
+
child.stdin.on("error", () => {
|
|
74893
|
+
});
|
|
74894
|
+
child.stdin.end(`${request}
|
|
74895
|
+
`);
|
|
74896
|
+
let completionResult;
|
|
74897
|
+
try {
|
|
74898
|
+
completionResult = await completion;
|
|
74899
|
+
} catch (error51) {
|
|
74900
|
+
throw new DeviceProviderError(
|
|
74901
|
+
`Unable to start the native device helper at ${binaryPath}`,
|
|
74902
|
+
"HELPER_START_FAILED",
|
|
74903
|
+
false,
|
|
74904
|
+
{ cause: error51 }
|
|
74905
|
+
);
|
|
74906
|
+
} finally {
|
|
74907
|
+
clearTimeout(timeout);
|
|
74908
|
+
}
|
|
74909
|
+
if (timedOut) {
|
|
74910
|
+
throw new DeviceProviderError(
|
|
74911
|
+
`Native device helper timed out after ${this.sidecarTimeoutMs}ms`,
|
|
74912
|
+
"HELPER_TIMEOUT",
|
|
74913
|
+
true
|
|
74914
|
+
);
|
|
74915
|
+
}
|
|
74916
|
+
if (outputExceeded) {
|
|
74917
|
+
throw new DeviceProviderError(
|
|
74918
|
+
`Native device helper output exceeded ${MAX_SIDECAR_OUTPUT_BYTES} bytes`,
|
|
74919
|
+
"OUTPUT_TOO_LARGE"
|
|
74920
|
+
);
|
|
74921
|
+
}
|
|
74922
|
+
const output = Buffer.concat(stdout).toString("utf8").trim();
|
|
74923
|
+
let response;
|
|
74924
|
+
try {
|
|
74925
|
+
response = JSON.parse(output);
|
|
74926
|
+
} catch (error51) {
|
|
74927
|
+
const detail = Buffer.concat(stderr).toString("utf8").trim();
|
|
74928
|
+
throw new DeviceProviderError(
|
|
74929
|
+
`Native device helper returned invalid JSON${detail ? `: ${detail}` : ""}`,
|
|
74930
|
+
"INVALID_RESPONSE",
|
|
74931
|
+
false,
|
|
74932
|
+
{ cause: error51 }
|
|
74933
|
+
);
|
|
74934
|
+
}
|
|
74935
|
+
if (response.protocolVersion !== DEVICE_SIDECAR_PROTOCOL_VERSION || response.id !== id || typeof response.ok !== "boolean") {
|
|
74936
|
+
throw new DeviceProviderError(
|
|
74937
|
+
"Native device helper returned a mismatched response envelope",
|
|
74938
|
+
"INVALID_RESPONSE"
|
|
74939
|
+
);
|
|
74940
|
+
}
|
|
74941
|
+
if (!response.ok) {
|
|
74942
|
+
throw new DeviceProviderError(
|
|
74943
|
+
response.error?.message || "Native device operation failed",
|
|
74944
|
+
response.error?.code || "OPERATION_FAILED",
|
|
74945
|
+
response.error?.retryable === true
|
|
74946
|
+
);
|
|
74947
|
+
}
|
|
74948
|
+
if (completionResult.code !== 0) {
|
|
74949
|
+
throw new DeviceProviderError(
|
|
74950
|
+
`Native device helper exited with status ${completionResult.code ?? completionResult.signal ?? "unknown"}`,
|
|
74951
|
+
"HELPER_EXITED"
|
|
74952
|
+
);
|
|
74953
|
+
}
|
|
74954
|
+
return response.result;
|
|
74955
|
+
}
|
|
74956
|
+
};
|
|
74957
|
+
|
|
74958
|
+
// src/commands/devices.ts
|
|
74959
|
+
var defaultDependencies3 = {
|
|
74960
|
+
ensureAuth,
|
|
74961
|
+
createClient: (apiKey) => createCliClient(apiKey),
|
|
74962
|
+
createProvider: () => new MacOSDeviceProvider(),
|
|
74963
|
+
getApiUrl,
|
|
74964
|
+
sleep: (milliseconds) => new Promise((resolve13) => setTimeout(resolve13, milliseconds)),
|
|
74965
|
+
setExitCode: (code) => {
|
|
74966
|
+
process.exitCode = code;
|
|
74967
|
+
}
|
|
74968
|
+
};
|
|
74969
|
+
function isRecord4(value) {
|
|
74970
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74971
|
+
}
|
|
74972
|
+
function unwrapData(response) {
|
|
74973
|
+
if (!isRecord4(response) || !("data" in response)) {
|
|
74974
|
+
throw new Error("Runtype API returned an invalid device response");
|
|
74975
|
+
}
|
|
74976
|
+
return response.data;
|
|
74977
|
+
}
|
|
74978
|
+
function errorMessage2(error51) {
|
|
74979
|
+
return error51 instanceof Error ? error51.message : String(error51);
|
|
74980
|
+
}
|
|
74981
|
+
function apiStatusCode(error51) {
|
|
74982
|
+
return isRecord4(error51) && typeof error51.statusCode === "number" ? error51.statusCode : null;
|
|
74983
|
+
}
|
|
74984
|
+
function isRecordedRevocationError(error51) {
|
|
74985
|
+
if (apiStatusCode(error51) !== 503) return false;
|
|
74986
|
+
const data = isRecord4(error51) && isRecord4(error51.data) ? error51.data : null;
|
|
74987
|
+
const detail = typeof data?.error === "string" ? data.error : errorMessage2(error51);
|
|
74988
|
+
return detail.toLowerCase().includes("revocation was recorded");
|
|
74989
|
+
}
|
|
74990
|
+
function printCommandError(label, error51) {
|
|
74991
|
+
console.error(chalk47.red(label));
|
|
74992
|
+
console.error(chalk47.red(errorMessage2(error51)));
|
|
74993
|
+
}
|
|
74994
|
+
function printDevice(device) {
|
|
74995
|
+
printDetail("Runtype Device", [
|
|
74996
|
+
{ label: "ID", value: device.id },
|
|
74997
|
+
{ label: "Name", value: device.name },
|
|
74998
|
+
{ label: "Platform", value: device.platform },
|
|
74999
|
+
{ label: "Online", value: device.status?.online },
|
|
75000
|
+
{ label: "Last seen", value: device.status?.lastSeenAt },
|
|
75001
|
+
{ label: "Available tools", value: device.status?.availableTools.join(", ") },
|
|
75002
|
+
{ label: "MCP URL", value: device.mcpUrl },
|
|
75003
|
+
{ label: "Revoked", value: device.revokedAt }
|
|
75004
|
+
]);
|
|
75005
|
+
}
|
|
75006
|
+
function localDeviceId(status) {
|
|
75007
|
+
return typeof status.deviceId === "string" && status.deviceId.length > 0 ? status.deviceId : null;
|
|
75008
|
+
}
|
|
75009
|
+
async function authenticatedClient(dependencies) {
|
|
75010
|
+
const apiKey = await dependencies.ensureAuth({ exitOnFailure: false });
|
|
75011
|
+
if (!apiKey) {
|
|
75012
|
+
dependencies.setExitCode(1);
|
|
75013
|
+
return null;
|
|
75014
|
+
}
|
|
75015
|
+
return dependencies.createClient(apiKey);
|
|
75016
|
+
}
|
|
75017
|
+
async function waitForDevice(client, deviceId, dependencies) {
|
|
75018
|
+
let latest = null;
|
|
75019
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
75020
|
+
try {
|
|
75021
|
+
latest = unwrapData(
|
|
75022
|
+
await client.get(`/devices/${encodeURIComponent(deviceId)}`)
|
|
75023
|
+
);
|
|
75024
|
+
if (latest.status?.online) return latest;
|
|
75025
|
+
} catch {
|
|
75026
|
+
}
|
|
75027
|
+
if (attempt < 7) await dependencies.sleep(750);
|
|
75028
|
+
}
|
|
75029
|
+
return latest;
|
|
75030
|
+
}
|
|
75031
|
+
async function runTail2(logPath, lines) {
|
|
75032
|
+
const child = spawn4("/usr/bin/tail", ["-n", String(lines), "-f", logPath], {
|
|
75033
|
+
stdio: "inherit",
|
|
75034
|
+
shell: false
|
|
75035
|
+
});
|
|
75036
|
+
return new Promise((resolve13, reject) => {
|
|
75037
|
+
child.once("error", reject);
|
|
75038
|
+
child.once("close", (code) => resolve13(code ?? 1));
|
|
75039
|
+
});
|
|
75040
|
+
}
|
|
75041
|
+
function createDevicesCommand(overrides = {}) {
|
|
75042
|
+
const dependencies = { ...defaultDependencies3, ...overrides };
|
|
75043
|
+
const command = new Command38("devices").description(
|
|
75044
|
+
"Manage local computers connected to Runtype agents"
|
|
75045
|
+
);
|
|
75046
|
+
command.command("setup").description("Install, pair, and start Runtype Device on this Mac").option("--name <name>", "Device name shown in Runtype").option("--helper-path <path>", "Install a locally built native helper (internal alpha)").option("--json", "Output as JSON").action(async (options) => {
|
|
75047
|
+
const provider = dependencies.createProvider();
|
|
75048
|
+
let pairedDeviceId = null;
|
|
75049
|
+
try {
|
|
75050
|
+
const installation = await provider.ensureInstalled({ helperPath: options.helperPath });
|
|
75051
|
+
const before = await provider.invoke("status");
|
|
75052
|
+
const existingDeviceId = localDeviceId(before);
|
|
75053
|
+
if (existingDeviceId) {
|
|
75054
|
+
throw new DeviceProviderError(
|
|
75055
|
+
`This Mac is already paired as ${existingDeviceId}. Run \`runtype devices status\` instead.`,
|
|
75056
|
+
"ALREADY_PAIRED"
|
|
75057
|
+
);
|
|
75058
|
+
}
|
|
75059
|
+
const client = await authenticatedClient(dependencies);
|
|
75060
|
+
if (!client) return;
|
|
75061
|
+
const pairing = unwrapData(
|
|
75062
|
+
await client.post("/devices/pairing-sessions", {})
|
|
75063
|
+
);
|
|
75064
|
+
const pairParams = {
|
|
75065
|
+
pairingUri: pairing.pairingUri,
|
|
75066
|
+
apiUrl: dependencies.getApiUrl()
|
|
75067
|
+
};
|
|
75068
|
+
if (options.name) pairParams.name = options.name;
|
|
75069
|
+
const paired = await provider.invoke("pair", pairParams);
|
|
75070
|
+
pairedDeviceId = paired.deviceId;
|
|
75071
|
+
const service = await provider.invoke("install");
|
|
75072
|
+
const local = await provider.invoke("status");
|
|
75073
|
+
const device = await waitForDevice(client, paired.deviceId, dependencies);
|
|
75074
|
+
const result = {
|
|
75075
|
+
deviceId: paired.deviceId,
|
|
75076
|
+
name: paired.name,
|
|
75077
|
+
online: device?.status?.online ?? false,
|
|
75078
|
+
mcpUrl: device?.mcpUrl ?? null,
|
|
75079
|
+
installation,
|
|
75080
|
+
service,
|
|
75081
|
+
local
|
|
75082
|
+
};
|
|
75083
|
+
if (options.json) {
|
|
75084
|
+
printJson(result);
|
|
75085
|
+
return;
|
|
75086
|
+
}
|
|
75087
|
+
console.log(chalk47.green(`Paired and started ${paired.name}`));
|
|
75088
|
+
console.log(` Device ID: ${chalk47.green(paired.deviceId)}`);
|
|
75089
|
+
console.log(
|
|
75090
|
+
` Relay: ${device?.status?.online ? chalk47.green("online") : chalk47.yellow("connecting")}`
|
|
75091
|
+
);
|
|
75092
|
+
if (device?.mcpUrl) console.log(` MCP URL: ${device.mcpUrl}`);
|
|
75093
|
+
if (local.messagesDatabaseReadable !== true) {
|
|
75094
|
+
console.log(
|
|
75095
|
+
chalk47.yellow(
|
|
75096
|
+
`
|
|
75097
|
+
Grant Full Disk Access to ${String(local.executablePath)} to enable iMessage reads.`
|
|
75098
|
+
)
|
|
75099
|
+
);
|
|
75100
|
+
}
|
|
75101
|
+
console.log(chalk47.gray("iMessage sending remains disabled by local policy."));
|
|
75102
|
+
} catch (error51) {
|
|
75103
|
+
printCommandError("Failed to set up Runtype Device", error51);
|
|
75104
|
+
if (pairedDeviceId) {
|
|
75105
|
+
console.error(
|
|
75106
|
+
chalk47.yellow(
|
|
75107
|
+
`Pairing completed as ${pairedDeviceId}, but startup did not finish. Run \`runtype devices install\` after correcting the error.`
|
|
75108
|
+
)
|
|
75109
|
+
);
|
|
75110
|
+
}
|
|
75111
|
+
dependencies.setExitCode(1);
|
|
75112
|
+
}
|
|
75113
|
+
});
|
|
75114
|
+
command.command("install").description("Install or restart the local Runtype Device background service").option("--helper-path <path>", "Install a locally built native helper (internal alpha)").option("--json", "Output as JSON").action(async (options) => {
|
|
75115
|
+
const provider = dependencies.createProvider();
|
|
75116
|
+
try {
|
|
75117
|
+
const installation = await provider.ensureInstalled({ helperPath: options.helperPath });
|
|
75118
|
+
const service = await provider.invoke("install");
|
|
75119
|
+
if (options.json) {
|
|
75120
|
+
printJson({ installation, service });
|
|
75121
|
+
} else {
|
|
75122
|
+
console.log(chalk47.green("Runtype Device is installed and running"));
|
|
75123
|
+
console.log(` Helper: ${installation.binaryPath}`);
|
|
75124
|
+
if (typeof service.logPath === "string") console.log(` Logs: ${service.logPath}`);
|
|
75125
|
+
}
|
|
75126
|
+
} catch (error51) {
|
|
75127
|
+
printCommandError("Failed to install Runtype Device", error51);
|
|
75128
|
+
dependencies.setExitCode(1);
|
|
75129
|
+
}
|
|
75130
|
+
});
|
|
75131
|
+
command.command("list").description("List paired devices and relay status").option("--json", "Output as JSON").action(async (options) => {
|
|
75132
|
+
try {
|
|
75133
|
+
const client = await authenticatedClient(dependencies);
|
|
75134
|
+
if (!client) return;
|
|
75135
|
+
const devices = unwrapData(await client.get("/devices"));
|
|
75136
|
+
if (options.json) {
|
|
75137
|
+
printJson({ data: devices });
|
|
75138
|
+
return;
|
|
75139
|
+
}
|
|
75140
|
+
if (devices.length === 0) {
|
|
75141
|
+
console.log(chalk47.gray("No paired devices"));
|
|
75142
|
+
return;
|
|
75143
|
+
}
|
|
75144
|
+
console.log(chalk47.cyan("Runtype Devices:"));
|
|
75145
|
+
for (const device of devices) {
|
|
75146
|
+
const state = device.revokedAt ? "revoked" : device.status?.online ? "online" : "offline";
|
|
75147
|
+
console.log(` ${chalk47.green(device.id)} ${device.name} ${state}`);
|
|
75148
|
+
}
|
|
75149
|
+
} catch (error51) {
|
|
75150
|
+
printCommandError("Failed to list Runtype devices", error51);
|
|
75151
|
+
dependencies.setExitCode(1);
|
|
75152
|
+
}
|
|
75153
|
+
});
|
|
75154
|
+
command.command("status [id]").description("Show local status, or cloud relay status for a device ID").option("--json", "Output as JSON").action(async (id, options) => {
|
|
75155
|
+
try {
|
|
75156
|
+
if (id) {
|
|
75157
|
+
const client = await authenticatedClient(dependencies);
|
|
75158
|
+
if (!client) return;
|
|
75159
|
+
const device = unwrapData(
|
|
75160
|
+
await client.get(`/devices/${encodeURIComponent(id)}`)
|
|
75161
|
+
);
|
|
75162
|
+
if (options.json) printJson(device);
|
|
75163
|
+
else printDevice(device);
|
|
75164
|
+
return;
|
|
75165
|
+
}
|
|
75166
|
+
const local = await dependencies.createProvider().invoke("status");
|
|
75167
|
+
if (options.json) {
|
|
75168
|
+
printJson(local);
|
|
75169
|
+
} else {
|
|
75170
|
+
printDetail("Local Runtype Device", [
|
|
75171
|
+
{ label: "Device ID", value: local.deviceId ?? "not paired" },
|
|
75172
|
+
{ label: "API URL", value: local.apiUrl },
|
|
75173
|
+
{ label: "Helper version", value: local.helperVersion },
|
|
75174
|
+
{ label: "Service installed", value: local.serviceInstalled },
|
|
75175
|
+
{ label: "Service running", value: local.serviceRunning },
|
|
75176
|
+
{ label: "Messages readable", value: local.messagesDatabaseReadable },
|
|
75177
|
+
{ label: "Sending enabled", value: local.allowSend },
|
|
75178
|
+
{ label: "Executable", value: local.executablePath },
|
|
75179
|
+
{ label: "Logs", value: local.logPath }
|
|
75180
|
+
]);
|
|
75181
|
+
}
|
|
75182
|
+
} catch (error51) {
|
|
75183
|
+
printCommandError("Failed to read Runtype Device status", error51);
|
|
75184
|
+
dependencies.setExitCode(1);
|
|
75185
|
+
}
|
|
75186
|
+
});
|
|
75187
|
+
command.command("doctor").description("Check native helper, background service, and Messages permissions").option("--json", "Output as JSON").action(async (options) => {
|
|
75188
|
+
const provider = dependencies.createProvider();
|
|
75189
|
+
try {
|
|
75190
|
+
const probe = await provider.probe();
|
|
75191
|
+
const local = probe.installed && !probe.reason ? await provider.invoke("doctor") : null;
|
|
75192
|
+
const healthy = probe.supported && probe.installed && local?.serviceRunning === true && local.messagesDatabaseReadable === true;
|
|
75193
|
+
const result = { healthy, probe, local };
|
|
75194
|
+
if (options.json) {
|
|
75195
|
+
printJson(result);
|
|
75196
|
+
} else {
|
|
75197
|
+
console.log(
|
|
75198
|
+
healthy ? chalk47.green("Runtype Device is healthy") : chalk47.yellow("Runtype Device needs attention")
|
|
75199
|
+
);
|
|
75200
|
+
console.log(` Platform supported: ${probe.supported ? "yes" : "no"}`);
|
|
75201
|
+
console.log(` Helper installed: ${probe.installed ? "yes" : "no"}`);
|
|
75202
|
+
if (probe.reason) console.log(` Helper: ${probe.reason}`);
|
|
75203
|
+
if (local) {
|
|
75204
|
+
console.log(` Service running: ${local.serviceRunning === true ? "yes" : "no"}`);
|
|
75205
|
+
console.log(
|
|
75206
|
+
` Messages readable: ${local.messagesDatabaseReadable === true ? "yes" : "no"}`
|
|
75207
|
+
);
|
|
75208
|
+
if (local.messagesDatabaseReadable !== true) {
|
|
75209
|
+
console.log(
|
|
75210
|
+
chalk47.yellow(` Grant Full Disk Access to ${String(local.executablePath)}`)
|
|
75211
|
+
);
|
|
75212
|
+
}
|
|
75213
|
+
}
|
|
75214
|
+
}
|
|
75215
|
+
if (!healthy) dependencies.setExitCode(1);
|
|
75216
|
+
} catch (error51) {
|
|
75217
|
+
printCommandError("Runtype Device diagnostics failed", error51);
|
|
75218
|
+
dependencies.setExitCode(1);
|
|
75219
|
+
}
|
|
75220
|
+
});
|
|
75221
|
+
command.command("policy <mode>").description("Set local iMessage send policy (allow-send or deny-send)").option("--json", "Output as JSON").action(async (mode, options) => {
|
|
75222
|
+
if (mode !== "allow-send" && mode !== "deny-send") {
|
|
75223
|
+
console.error(chalk47.red("Policy must be allow-send or deny-send"));
|
|
75224
|
+
dependencies.setExitCode(1);
|
|
75225
|
+
return;
|
|
75226
|
+
}
|
|
75227
|
+
const provider = dependencies.createProvider();
|
|
75228
|
+
let policyUpdated = false;
|
|
75229
|
+
try {
|
|
75230
|
+
const policy = await provider.invoke("policy", {
|
|
75231
|
+
allowSend: mode === "allow-send"
|
|
75232
|
+
});
|
|
75233
|
+
policyUpdated = true;
|
|
75234
|
+
const status = await provider.invoke("status");
|
|
75235
|
+
const service = status.serviceInstalled === true ? await provider.invoke("install") : null;
|
|
75236
|
+
const result = {
|
|
75237
|
+
...policy,
|
|
75238
|
+
capabilitiesRepublished: service !== null,
|
|
75239
|
+
service
|
|
75240
|
+
};
|
|
75241
|
+
if (options.json) printJson(result);
|
|
75242
|
+
else {
|
|
75243
|
+
console.log(
|
|
75244
|
+
chalk47.green(
|
|
75245
|
+
`Local iMessage sending is ${mode === "allow-send" ? "enabled" : "disabled"}`
|
|
75246
|
+
)
|
|
75247
|
+
);
|
|
75248
|
+
if (service)
|
|
75249
|
+
console.log(chalk47.gray("Restarted the device service to publish capabilities."));
|
|
75250
|
+
else {
|
|
75251
|
+
console.log(
|
|
75252
|
+
chalk47.yellow(
|
|
75253
|
+
"The device service is not installed; run `runtype devices install` to publish this policy."
|
|
75254
|
+
)
|
|
75255
|
+
);
|
|
75256
|
+
}
|
|
75257
|
+
}
|
|
75258
|
+
} catch (error51) {
|
|
75259
|
+
printCommandError(
|
|
75260
|
+
policyUpdated ? "Local policy was updated, but the device service could not publish it" : "Failed to update local device policy",
|
|
75261
|
+
error51
|
|
75262
|
+
);
|
|
75263
|
+
dependencies.setExitCode(1);
|
|
75264
|
+
}
|
|
75265
|
+
});
|
|
75266
|
+
command.command("mcp-url <id>").description("Print the managed MCP endpoint for a paired device").option("--json", "Output as JSON").action(async (id, options) => {
|
|
75267
|
+
try {
|
|
75268
|
+
const client = await authenticatedClient(dependencies);
|
|
75269
|
+
if (!client) return;
|
|
75270
|
+
const device = unwrapData(
|
|
75271
|
+
await client.get(`/devices/${encodeURIComponent(id)}`)
|
|
75272
|
+
);
|
|
75273
|
+
if (options.json) printJson({ deviceId: id, mcpUrl: device.mcpUrl ?? null });
|
|
75274
|
+
else if (device.mcpUrl) console.log(device.mcpUrl);
|
|
75275
|
+
else throw new Error("The API did not return an MCP URL for this device");
|
|
75276
|
+
} catch (error51) {
|
|
75277
|
+
printCommandError("Failed to get device MCP URL", error51);
|
|
75278
|
+
dependencies.setExitCode(1);
|
|
75279
|
+
}
|
|
75280
|
+
});
|
|
75281
|
+
command.command("revoke <id>").description("Revoke a paired device in Runtype").option("--yes", "Confirm irreversible device revocation").option("--json", "Output as JSON").action(async (id, options) => {
|
|
75282
|
+
if (!options.yes) {
|
|
75283
|
+
console.error(chalk47.red("Pass --yes to confirm irreversible device revocation"));
|
|
75284
|
+
dependencies.setExitCode(1);
|
|
75285
|
+
return;
|
|
75286
|
+
}
|
|
75287
|
+
try {
|
|
75288
|
+
const client = await authenticatedClient(dependencies);
|
|
75289
|
+
if (!client) return;
|
|
75290
|
+
const device = unwrapData(
|
|
75291
|
+
await client.post(`/devices/${encodeURIComponent(id)}/revoke`, {})
|
|
75292
|
+
);
|
|
75293
|
+
if (options.json) printJson(device);
|
|
75294
|
+
else {
|
|
75295
|
+
console.log(chalk47.green(`Revoked ${device.name} (${device.id})`));
|
|
75296
|
+
console.log(chalk47.gray("If this is the local Mac, run `runtype devices reset --yes`."));
|
|
75297
|
+
}
|
|
75298
|
+
} catch (error51) {
|
|
75299
|
+
if (isRecordedRevocationError(error51)) {
|
|
75300
|
+
const result = {
|
|
75301
|
+
deviceId: id,
|
|
75302
|
+
revoked: true,
|
|
75303
|
+
relayDisconnectConfirmed: false,
|
|
75304
|
+
message: errorMessage2(error51)
|
|
75305
|
+
};
|
|
75306
|
+
if (options.json) printJson(result);
|
|
75307
|
+
else {
|
|
75308
|
+
console.error(
|
|
75309
|
+
chalk47.yellow(
|
|
75310
|
+
`Revocation was recorded for ${id}, but relay disconnection was not confirmed.`
|
|
75311
|
+
)
|
|
75312
|
+
);
|
|
75313
|
+
console.error(
|
|
75314
|
+
chalk47.yellow("Retry this command, and reset the local Mac if applicable.")
|
|
75315
|
+
);
|
|
75316
|
+
}
|
|
75317
|
+
dependencies.setExitCode(1);
|
|
75318
|
+
return;
|
|
75319
|
+
}
|
|
75320
|
+
printCommandError("Failed to revoke Runtype device", error51);
|
|
75321
|
+
dependencies.setExitCode(1);
|
|
75322
|
+
}
|
|
75323
|
+
});
|
|
75324
|
+
command.command("logs").description("Read local Runtype Device logs").option("-n, --lines <count>", "Number of lines", "100").option("-f, --follow", "Follow the log file").action(async (options) => {
|
|
75325
|
+
try {
|
|
75326
|
+
const lines = Number.parseInt(options.lines, 10);
|
|
75327
|
+
if (!Number.isInteger(lines) || lines < 1 || lines > 1e4) {
|
|
75328
|
+
throw new Error("--lines must be an integer between 1 and 10000");
|
|
75329
|
+
}
|
|
75330
|
+
const status = await dependencies.createProvider().invoke("status");
|
|
75331
|
+
if (typeof status.logPath !== "string")
|
|
75332
|
+
throw new Error("The helper did not return a log path");
|
|
75333
|
+
if (options.follow) {
|
|
75334
|
+
const code = await runTail2(status.logPath, lines);
|
|
75335
|
+
if (code !== 0) dependencies.setExitCode(code);
|
|
75336
|
+
return;
|
|
75337
|
+
}
|
|
75338
|
+
const content = await readFile2(status.logPath, "utf8").catch((error51) => {
|
|
75339
|
+
const code = isRecord4(error51) ? error51.code : void 0;
|
|
75340
|
+
if (code === "ENOENT") return "";
|
|
75341
|
+
throw error51;
|
|
75342
|
+
});
|
|
75343
|
+
const entries = content.split(/\r?\n/u);
|
|
75344
|
+
if (entries.at(-1) === "") entries.pop();
|
|
75345
|
+
const selected = entries.slice(-lines).join("\n");
|
|
75346
|
+
if (selected) console.log(selected);
|
|
75347
|
+
else console.log(chalk47.gray("No local device logs yet"));
|
|
75348
|
+
} catch (error51) {
|
|
75349
|
+
printCommandError("Failed to read Runtype Device logs", error51);
|
|
75350
|
+
dependencies.setExitCode(1);
|
|
75351
|
+
}
|
|
75352
|
+
});
|
|
75353
|
+
command.command("uninstall").description("Stop the local background service while preserving pairing state").option("--remove-binary", "Also remove the CLI-managed helper binary").option("--json", "Output as JSON").action(async (options) => {
|
|
75354
|
+
const provider = dependencies.createProvider();
|
|
75355
|
+
try {
|
|
75356
|
+
const service = await provider.invoke("uninstall");
|
|
75357
|
+
const removedBinary = options.removeBinary ? await provider.removeInstalledBinary() : false;
|
|
75358
|
+
if (options.json) printJson({ service, removedBinary });
|
|
75359
|
+
else console.log(chalk47.green("Stopped and uninstalled the local Runtype Device service"));
|
|
75360
|
+
} catch (error51) {
|
|
75361
|
+
printCommandError("Failed to uninstall Runtype Device", error51);
|
|
75362
|
+
dependencies.setExitCode(1);
|
|
75363
|
+
}
|
|
75364
|
+
});
|
|
75365
|
+
command.command("reset").description("Clear local pairing state and rotate the native signing identity").option("--yes", "Confirm destructive local reset").option("--json", "Output as JSON").action(async (options) => {
|
|
75366
|
+
if (!options.yes) {
|
|
75367
|
+
console.error(chalk47.red("Pass --yes to confirm destructive local reset"));
|
|
75368
|
+
dependencies.setExitCode(1);
|
|
75369
|
+
return;
|
|
75370
|
+
}
|
|
75371
|
+
try {
|
|
75372
|
+
const result = await dependencies.createProvider().invoke("reset", { rotateIdentity: true });
|
|
75373
|
+
if (options.json) printJson(result);
|
|
75374
|
+
else {
|
|
75375
|
+
console.log(chalk47.green("Cleared local device state and rotated the signing identity"));
|
|
75376
|
+
console.log(
|
|
75377
|
+
chalk47.gray("Revoke the old device in Runtype if it has not already been revoked.")
|
|
75378
|
+
);
|
|
75379
|
+
}
|
|
75380
|
+
} catch (error51) {
|
|
75381
|
+
printCommandError("Failed to reset local Runtype Device state", error51);
|
|
75382
|
+
dependencies.setExitCode(1);
|
|
75383
|
+
}
|
|
75384
|
+
});
|
|
75385
|
+
return command;
|
|
75386
|
+
}
|
|
75387
|
+
var devicesCommand = createDevicesCommand();
|
|
75388
|
+
|
|
74193
75389
|
// src/index.ts
|
|
74194
75390
|
init_credential_store();
|
|
74195
75391
|
|
|
@@ -74209,7 +75405,7 @@ function registerGlobalOptionForwarding(program2) {
|
|
|
74209
75405
|
// src/index.ts
|
|
74210
75406
|
loadEnv();
|
|
74211
75407
|
setCliTitle();
|
|
74212
|
-
var program = new
|
|
75408
|
+
var program = new Command39();
|
|
74213
75409
|
program.name("runtype").description("CLI for Runtype AI Platform").version(getCliVersion()).option("-v, --verbose", "Enable verbose output").option("--api-url <url>", "Override API URL").option("--json", "Output as JSON");
|
|
74214
75410
|
program.addCommand(initCommand);
|
|
74215
75411
|
program.addCommand(loginCommand);
|
|
@@ -74246,7 +75442,9 @@ program.addCommand(deployCommand);
|
|
|
74246
75442
|
program.addCommand(skillsCommand);
|
|
74247
75443
|
program.addCommand(updateCommand);
|
|
74248
75444
|
program.addCommand(installMcpCommand);
|
|
75445
|
+
program.addCommand(onboardCommand);
|
|
74249
75446
|
program.addCommand(mcpCommand);
|
|
75447
|
+
program.addCommand(devicesCommand);
|
|
74250
75448
|
program.addCommand(appsCommand, { hidden: process.env.RUNTYPE_APPS_CLI !== "1" });
|
|
74251
75449
|
registerGlobalOptionForwarding(program);
|
|
74252
75450
|
program.exitOverride();
|
|
@@ -74279,17 +75477,17 @@ async function run() {
|
|
|
74279
75477
|
run().catch((error51) => {
|
|
74280
75478
|
const commanderError = error51;
|
|
74281
75479
|
if (commanderError.code === "commander.missingArgument") {
|
|
74282
|
-
console.error(
|
|
75480
|
+
console.error(chalk48.red(`Error: ${commanderError.message}`));
|
|
74283
75481
|
process.exit(1);
|
|
74284
75482
|
} else if (commanderError.code === "commander.unknownOption") {
|
|
74285
|
-
console.error(
|
|
75483
|
+
console.error(chalk48.red(`Error: ${commanderError.message}`));
|
|
74286
75484
|
process.exit(1);
|
|
74287
75485
|
} else if (commanderError.code === "commander.unknownCommand") {
|
|
74288
75486
|
process.exit(1);
|
|
74289
75487
|
} else if (commanderError.code === "commander.help" || commanderError.code === "commander.helpDisplayed" || commanderError.code === "commander.version") {
|
|
74290
75488
|
process.exit(0);
|
|
74291
75489
|
} else {
|
|
74292
|
-
console.error(
|
|
75490
|
+
console.error(chalk48.red("An unexpected error occurred:"));
|
|
74293
75491
|
console.error(error51);
|
|
74294
75492
|
process.exit(1);
|
|
74295
75493
|
}
|
|
@@ -74298,12 +75496,12 @@ async function handleNoCommand() {
|
|
|
74298
75496
|
const store = new CredentialStore();
|
|
74299
75497
|
const hasCredentials = await store.hasCredentials();
|
|
74300
75498
|
if (!hasCredentials) {
|
|
74301
|
-
console.log(
|
|
75499
|
+
console.log(chalk48.cyan("\nWelcome to Runtype CLI!\n"));
|
|
74302
75500
|
console.log("It looks like this is your first time. Run the setup wizard:");
|
|
74303
|
-
console.log(` ${
|
|
75501
|
+
console.log(` ${chalk48.green("runtype init")}
|
|
74304
75502
|
`);
|
|
74305
75503
|
console.log("Or see all available commands:");
|
|
74306
|
-
console.log(` ${
|
|
75504
|
+
console.log(` ${chalk48.green("runtype --help")}
|
|
74307
75505
|
`);
|
|
74308
75506
|
} else {
|
|
74309
75507
|
try {
|