@runtypelabs/cli 2.28.1 → 2.29.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 +929 -106
- 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 Command38 } from "commander";
|
|
140
|
+
import chalk47 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;
|
|
@@ -42050,8 +42050,8 @@ var FLAT_ADVANCED_CONFIG_KEYS = {
|
|
|
42050
42050
|
var FLAT_ADVANCED_CONFIG_KEY_LIST = Object.keys(
|
|
42051
42051
|
FLAT_ADVANCED_CONFIG_KEYS
|
|
42052
42052
|
);
|
|
42053
|
-
function createIssue(severity, code, message,
|
|
42054
|
-
return { code, message, path:
|
|
42053
|
+
function createIssue(severity, code, message, path20, suggestedFix) {
|
|
42054
|
+
return { code, message, path: path20, severity, ...suggestedFix ? { suggestedFix } : {} };
|
|
42055
42055
|
}
|
|
42056
42056
|
function emptyResult() {
|
|
42057
42057
|
return { valid: true, errors: [], warnings: [], recommendations: [] };
|
|
@@ -42886,16 +42886,16 @@ var FLOW_PREFIX = Object.values(RUNTIME_PREFIXES).find((spec) => spec.namespace
|
|
|
42886
42886
|
var SECRET_PREFIX = Object.values(RUNTIME_PREFIXES).find(
|
|
42887
42887
|
(spec) => spec.namespace === "secret"
|
|
42888
42888
|
).prefix;
|
|
42889
|
-
function collectStringLeafPaths(value,
|
|
42889
|
+
function collectStringLeafPaths(value, path20 = []) {
|
|
42890
42890
|
if (typeof value === "string") {
|
|
42891
|
-
return [{ path:
|
|
42891
|
+
return [{ path: path20, value }];
|
|
42892
42892
|
}
|
|
42893
42893
|
if (Array.isArray(value)) {
|
|
42894
|
-
return value.flatMap((item, index) => collectStringLeafPaths(item, [...
|
|
42894
|
+
return value.flatMap((item, index) => collectStringLeafPaths(item, [...path20, String(index)]));
|
|
42895
42895
|
}
|
|
42896
42896
|
if (value && typeof value === "object") {
|
|
42897
42897
|
return Object.entries(value).flatMap(
|
|
42898
|
-
([key, nestedValue]) => collectStringLeafPaths(nestedValue, [...
|
|
42898
|
+
([key, nestedValue]) => collectStringLeafPaths(nestedValue, [...path20, key])
|
|
42899
42899
|
);
|
|
42900
42900
|
}
|
|
42901
42901
|
return [];
|
|
@@ -46656,13 +46656,13 @@ function parseJsonObject(raw, label) {
|
|
|
46656
46656
|
}
|
|
46657
46657
|
return parsed;
|
|
46658
46658
|
}
|
|
46659
|
-
function readJsonFile(
|
|
46659
|
+
function readJsonFile(path20, opts = {}) {
|
|
46660
46660
|
const label = opts.label ?? "file";
|
|
46661
46661
|
let raw;
|
|
46662
46662
|
try {
|
|
46663
|
-
raw = readFileSync3(
|
|
46663
|
+
raw = readFileSync3(path20, "utf-8");
|
|
46664
46664
|
} catch {
|
|
46665
|
-
console.error(chalk2.red(`Failed to read ${label}: ${
|
|
46665
|
+
console.error(chalk2.red(`Failed to read ${label}: ${path20}`));
|
|
46666
46666
|
process.exit(1);
|
|
46667
46667
|
}
|
|
46668
46668
|
const obj = parseJsonObject(raw, label);
|
|
@@ -61023,7 +61023,7 @@ function mergeMarathonSessionSummaries(existingSessions, nextSessions, offset) {
|
|
|
61023
61023
|
}
|
|
61024
61024
|
return Array.from(byIndex.values()).sort((left, right) => left.index - right.index);
|
|
61025
61025
|
}
|
|
61026
|
-
function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status = "complete",
|
|
61026
|
+
function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status = "complete", errorMessage3) {
|
|
61027
61027
|
return {
|
|
61028
61028
|
sessionIndex: sessionSummary.index,
|
|
61029
61029
|
status,
|
|
@@ -61042,7 +61042,7 @@ function buildMarathonSessionSnapshot(liveState, sessionSummary, model, status =
|
|
|
61042
61042
|
...event,
|
|
61043
61043
|
data: structuredClone(event.data)
|
|
61044
61044
|
})),
|
|
61045
|
-
...
|
|
61045
|
+
...errorMessage3 ? { errorMessage: errorMessage3 } : {}
|
|
61046
61046
|
};
|
|
61047
61047
|
}
|
|
61048
61048
|
function mapRunTaskStatusToSnapshotStatus(status, fallback) {
|
|
@@ -61170,9 +61170,9 @@ function findLatestStateFile(stateDir) {
|
|
|
61170
61170
|
const files = fs8.readdirSync(dir).filter((f2) => f2.endsWith(".json"));
|
|
61171
61171
|
for (const file2 of files) {
|
|
61172
61172
|
const fullPath = path9.join(dir, file2);
|
|
61173
|
-
const
|
|
61174
|
-
if (!latest ||
|
|
61175
|
-
latest = { name: file2.replace(".json", ""), filePath: fullPath, mtime:
|
|
61173
|
+
const stat2 = fs8.statSync(fullPath);
|
|
61174
|
+
if (!latest || stat2.mtimeMs > latest.mtime) {
|
|
61175
|
+
latest = { name: file2.replace(".json", ""), filePath: fullPath, mtime: stat2.mtimeMs };
|
|
61176
61176
|
}
|
|
61177
61177
|
}
|
|
61178
61178
|
}
|
|
@@ -61504,8 +61504,8 @@ function safeReadTextFile(filePath) {
|
|
|
61504
61504
|
try {
|
|
61505
61505
|
const normalized = normalizeToolPath(filePath).replace(/\\/g, "/");
|
|
61506
61506
|
if (isSensitivePath(normalized)) return null;
|
|
61507
|
-
const
|
|
61508
|
-
if (!
|
|
61507
|
+
const stat2 = fs9.statSync(filePath);
|
|
61508
|
+
if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES_TO_SCAN) return null;
|
|
61509
61509
|
const buffer = fs9.readFileSync(filePath);
|
|
61510
61510
|
if (buffer.includes(0)) return null;
|
|
61511
61511
|
return buffer.toString("utf-8");
|
|
@@ -61672,14 +61672,14 @@ function resolveToolPath(toolPath, options = {}) {
|
|
|
61672
61672
|
}
|
|
61673
61673
|
}
|
|
61674
61674
|
if (resolved.exists) {
|
|
61675
|
-
const
|
|
61676
|
-
if (options.requireFile && !
|
|
61675
|
+
const stat2 = fs10.statSync(resolved.canonicalPath);
|
|
61676
|
+
if (options.requireFile && !stat2.isFile()) {
|
|
61677
61677
|
return {
|
|
61678
61678
|
ok: false,
|
|
61679
61679
|
error: `Path is not a file: ${requestedPath}`
|
|
61680
61680
|
};
|
|
61681
61681
|
}
|
|
61682
|
-
if (options.requireDirectory && !
|
|
61682
|
+
if (options.requireDirectory && !stat2.isDirectory()) {
|
|
61683
61683
|
return {
|
|
61684
61684
|
ok: false,
|
|
61685
61685
|
error: `Path is not a directory: ${requestedPath}`
|
|
@@ -67092,8 +67092,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
67092
67092
|
const client = createCliClient(apiKey);
|
|
67093
67093
|
if (!isTTY(options) || options.json) {
|
|
67094
67094
|
try {
|
|
67095
|
-
const
|
|
67096
|
-
const data = await client.get(
|
|
67095
|
+
const path20 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
67096
|
+
const data = await client.get(path20);
|
|
67097
67097
|
printJson(data);
|
|
67098
67098
|
} catch (error51) {
|
|
67099
67099
|
const message = error51 instanceof Error ? error51.message : "Unknown error";
|
|
@@ -67110,8 +67110,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
67110
67110
|
useEffect32(() => {
|
|
67111
67111
|
const run2 = async () => {
|
|
67112
67112
|
try {
|
|
67113
|
-
const
|
|
67114
|
-
const data = await client.get(
|
|
67113
|
+
const path20 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
67114
|
+
const data = await client.get(path20);
|
|
67115
67115
|
printJson(data);
|
|
67116
67116
|
setSuccess(true);
|
|
67117
67117
|
setLoading(false);
|
|
@@ -72830,9 +72830,9 @@ function collectBundleFiles(dir) {
|
|
|
72830
72830
|
for (const entry of readdirSync7(current)) {
|
|
72831
72831
|
if (entry.startsWith(".")) continue;
|
|
72832
72832
|
const full = join14(current, entry);
|
|
72833
|
-
const
|
|
72834
|
-
if (
|
|
72835
|
-
if (
|
|
72833
|
+
const stat2 = lstatSync(full);
|
|
72834
|
+
if (stat2.isSymbolicLink()) continue;
|
|
72835
|
+
if (stat2.isDirectory()) {
|
|
72836
72836
|
if (entry === "node_modules") continue;
|
|
72837
72837
|
walk(full);
|
|
72838
72838
|
} else {
|
|
@@ -73310,11 +73310,11 @@ function isHealthyManagedVersion(paths, version2) {
|
|
|
73310
73310
|
return readInstalledPackageVersion(packageRoot) === version2 && existsSync17(getVersionEntrypoint(paths, version2));
|
|
73311
73311
|
}
|
|
73312
73312
|
function isPathInside(parent, child) {
|
|
73313
|
-
const canonicalize = (
|
|
73313
|
+
const canonicalize = (path20) => {
|
|
73314
73314
|
try {
|
|
73315
|
-
return realpathSync3(
|
|
73315
|
+
return realpathSync3(path20);
|
|
73316
73316
|
} catch {
|
|
73317
|
-
return resolve12(
|
|
73317
|
+
return resolve12(path20);
|
|
73318
73318
|
}
|
|
73319
73319
|
};
|
|
73320
73320
|
const relativePath = relative8(canonicalize(parent), canonicalize(child));
|
|
@@ -74012,9 +74012,9 @@ async function resolveMcpArguments(options, dependencies = {}) {
|
|
|
74012
74012
|
for (const typedField of typedFields) {
|
|
74013
74013
|
const [key, rawValue] = splitField(typedField, "-F");
|
|
74014
74014
|
if (rawValue.startsWith("@")) {
|
|
74015
|
-
const
|
|
74016
|
-
if (!
|
|
74017
|
-
const fileValue =
|
|
74015
|
+
const path20 = rawValue.slice(1);
|
|
74016
|
+
if (!path20) throw new Error("-F file references require @<path> or @-");
|
|
74017
|
+
const fileValue = path20 === "-" ? await deps.readStdin() : await deps.readFile(path20, "utf8");
|
|
74018
74018
|
setField(key, parseTypedValue(fileValue));
|
|
74019
74019
|
} else {
|
|
74020
74020
|
setField(key, parseTypedValue(rawValue));
|
|
@@ -74190,6 +74190,828 @@ function createMcpCommand(overrides = {}) {
|
|
|
74190
74190
|
}
|
|
74191
74191
|
var mcpCommand = createMcpCommand();
|
|
74192
74192
|
|
|
74193
|
+
// src/commands/devices.ts
|
|
74194
|
+
import { spawn as spawn4 } from "child_process";
|
|
74195
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
74196
|
+
import { Command as Command37 } from "commander";
|
|
74197
|
+
import chalk46 from "chalk";
|
|
74198
|
+
|
|
74199
|
+
// src/lib/device-provider.ts
|
|
74200
|
+
var DEVICE_SIDECAR_PROTOCOL_VERSION = 1;
|
|
74201
|
+
var DeviceProviderError = class extends Error {
|
|
74202
|
+
constructor(message, code, retryable = false, options) {
|
|
74203
|
+
super(message, options);
|
|
74204
|
+
this.code = code;
|
|
74205
|
+
this.retryable = retryable;
|
|
74206
|
+
this.name = "DeviceProviderError";
|
|
74207
|
+
}
|
|
74208
|
+
code;
|
|
74209
|
+
retryable;
|
|
74210
|
+
};
|
|
74211
|
+
|
|
74212
|
+
// src/lib/macos-device-provider.ts
|
|
74213
|
+
import { spawn as spawn3 } from "child_process";
|
|
74214
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
74215
|
+
import { constants as fsConstants } from "fs";
|
|
74216
|
+
import { access, chmod, copyFile, mkdir, realpath, rename, rm, stat } from "fs/promises";
|
|
74217
|
+
import os6 from "os";
|
|
74218
|
+
import path19 from "path";
|
|
74219
|
+
var MAX_SIDECAR_OUTPUT_BYTES = 1048576;
|
|
74220
|
+
var MAX_HELPER_BYTES = 100 * 1048576;
|
|
74221
|
+
var DEFAULT_SIDECAR_TIMEOUT_MS = 3e4;
|
|
74222
|
+
function isRecord3(value) {
|
|
74223
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74224
|
+
}
|
|
74225
|
+
function parseHello(value) {
|
|
74226
|
+
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")) {
|
|
74227
|
+
throw new DeviceProviderError(
|
|
74228
|
+
"The native helper returned an invalid hello response",
|
|
74229
|
+
"INVALID_RESPONSE"
|
|
74230
|
+
);
|
|
74231
|
+
}
|
|
74232
|
+
return value;
|
|
74233
|
+
}
|
|
74234
|
+
async function isExecutable(filePath) {
|
|
74235
|
+
try {
|
|
74236
|
+
await access(filePath, fsConstants.X_OK);
|
|
74237
|
+
const details = await stat(filePath);
|
|
74238
|
+
return details.isFile();
|
|
74239
|
+
} catch {
|
|
74240
|
+
return false;
|
|
74241
|
+
}
|
|
74242
|
+
}
|
|
74243
|
+
var MacOSDeviceProvider = class {
|
|
74244
|
+
id = "com.runtype.device.macos";
|
|
74245
|
+
platform;
|
|
74246
|
+
architecture;
|
|
74247
|
+
operatingSystemRelease;
|
|
74248
|
+
homeDirectory;
|
|
74249
|
+
workingDirectory;
|
|
74250
|
+
environment;
|
|
74251
|
+
sidecarTimeoutMs;
|
|
74252
|
+
constructor(options = {}) {
|
|
74253
|
+
this.platform = options.platform ?? process.platform;
|
|
74254
|
+
this.architecture = options.architecture ?? process.arch;
|
|
74255
|
+
this.operatingSystemRelease = options.operatingSystemRelease ?? os6.release();
|
|
74256
|
+
this.homeDirectory = options.homeDirectory ?? os6.homedir();
|
|
74257
|
+
this.workingDirectory = options.workingDirectory ?? process.cwd();
|
|
74258
|
+
this.environment = options.environment ?? process.env;
|
|
74259
|
+
this.sidecarTimeoutMs = options.sidecarTimeoutMs ?? DEFAULT_SIDECAR_TIMEOUT_MS;
|
|
74260
|
+
}
|
|
74261
|
+
async probe() {
|
|
74262
|
+
const unsupportedReason = this.unsupportedReason();
|
|
74263
|
+
if (unsupportedReason) {
|
|
74264
|
+
return {
|
|
74265
|
+
providerId: this.id,
|
|
74266
|
+
supported: false,
|
|
74267
|
+
installed: false,
|
|
74268
|
+
reason: unsupportedReason
|
|
74269
|
+
};
|
|
74270
|
+
}
|
|
74271
|
+
const binaryPath = await this.resolveBinary();
|
|
74272
|
+
if (!binaryPath) {
|
|
74273
|
+
return {
|
|
74274
|
+
providerId: this.id,
|
|
74275
|
+
supported: true,
|
|
74276
|
+
installed: false,
|
|
74277
|
+
reason: "The Runtype macOS device helper is not installed"
|
|
74278
|
+
};
|
|
74279
|
+
}
|
|
74280
|
+
try {
|
|
74281
|
+
const hello = parseHello(await this.invokeAt(binaryPath, "hello"));
|
|
74282
|
+
this.assertCompatible(hello);
|
|
74283
|
+
return {
|
|
74284
|
+
providerId: this.id,
|
|
74285
|
+
supported: true,
|
|
74286
|
+
installed: true,
|
|
74287
|
+
binaryPath,
|
|
74288
|
+
helperVersion: hello.helperVersion,
|
|
74289
|
+
protocolVersion: hello.protocolVersion
|
|
74290
|
+
};
|
|
74291
|
+
} catch (error51) {
|
|
74292
|
+
return {
|
|
74293
|
+
providerId: this.id,
|
|
74294
|
+
supported: true,
|
|
74295
|
+
installed: true,
|
|
74296
|
+
binaryPath,
|
|
74297
|
+
reason: error51 instanceof Error ? error51.message : String(error51)
|
|
74298
|
+
};
|
|
74299
|
+
}
|
|
74300
|
+
}
|
|
74301
|
+
async ensureInstalled(options = {}) {
|
|
74302
|
+
this.assertPlatform();
|
|
74303
|
+
if (!options.helperPath) {
|
|
74304
|
+
const installedPath = await this.resolveInstalledBinary();
|
|
74305
|
+
if (installedPath) {
|
|
74306
|
+
return this.describeInstallation(installedPath, false);
|
|
74307
|
+
}
|
|
74308
|
+
}
|
|
74309
|
+
const sourcePath = options.helperPath ? path19.resolve(options.helperPath) : await this.resolveDevelopmentSource();
|
|
74310
|
+
if (!sourcePath) {
|
|
74311
|
+
throw new DeviceProviderError(
|
|
74312
|
+
[
|
|
74313
|
+
"The Runtype macOS device helper is not installed.",
|
|
74314
|
+
"For this internal-alpha build, compile it from the core repository and pass:",
|
|
74315
|
+
" runtype devices setup --helper-path apps/device-daemon-macos/.build/release/runtype-device"
|
|
74316
|
+
].join("\n"),
|
|
74317
|
+
"HELPER_NOT_INSTALLED"
|
|
74318
|
+
);
|
|
74319
|
+
}
|
|
74320
|
+
const resolvedSource = await realpath(sourcePath).catch(() => sourcePath);
|
|
74321
|
+
const sourceDetails = await stat(resolvedSource).catch(() => null);
|
|
74322
|
+
if (!sourceDetails?.isFile() || sourceDetails.size > MAX_HELPER_BYTES) {
|
|
74323
|
+
throw new DeviceProviderError(
|
|
74324
|
+
`Device helper is not a valid executable file: ${sourcePath}`,
|
|
74325
|
+
"INVALID_HELPER"
|
|
74326
|
+
);
|
|
74327
|
+
}
|
|
74328
|
+
await access(resolvedSource, fsConstants.X_OK).catch((error51) => {
|
|
74329
|
+
throw new DeviceProviderError(
|
|
74330
|
+
`Device helper is not executable: ${sourcePath}`,
|
|
74331
|
+
"INVALID_HELPER",
|
|
74332
|
+
false,
|
|
74333
|
+
{ cause: error51 }
|
|
74334
|
+
);
|
|
74335
|
+
});
|
|
74336
|
+
const sourceHello = parseHello(await this.invokeAt(resolvedSource, "hello"));
|
|
74337
|
+
this.assertCompatible(sourceHello);
|
|
74338
|
+
const targetPath = this.stableBinaryPath;
|
|
74339
|
+
const resolvedTarget = await realpath(targetPath).catch(() => targetPath);
|
|
74340
|
+
if (resolvedSource !== resolvedTarget) {
|
|
74341
|
+
const targetDirectory = path19.dirname(targetPath);
|
|
74342
|
+
await mkdir(targetDirectory, { recursive: true, mode: 448 });
|
|
74343
|
+
await chmod(targetDirectory, 448);
|
|
74344
|
+
const temporaryPath = path19.join(targetDirectory, `.runtype-device-${randomUUID2()}.tmp`);
|
|
74345
|
+
try {
|
|
74346
|
+
await copyFile(resolvedSource, temporaryPath, fsConstants.COPYFILE_EXCL);
|
|
74347
|
+
await chmod(temporaryPath, 448);
|
|
74348
|
+
await rename(temporaryPath, targetPath);
|
|
74349
|
+
} finally {
|
|
74350
|
+
await rm(temporaryPath, { force: true });
|
|
74351
|
+
}
|
|
74352
|
+
}
|
|
74353
|
+
return this.describeInstallation(targetPath, true);
|
|
74354
|
+
}
|
|
74355
|
+
async invoke(method, params = {}) {
|
|
74356
|
+
this.assertPlatform();
|
|
74357
|
+
const binaryPath = await this.resolveBinary();
|
|
74358
|
+
if (!binaryPath) {
|
|
74359
|
+
throw new DeviceProviderError(
|
|
74360
|
+
"The Runtype macOS device helper is not installed. Run `runtype devices setup` first.",
|
|
74361
|
+
"HELPER_NOT_INSTALLED"
|
|
74362
|
+
);
|
|
74363
|
+
}
|
|
74364
|
+
const result = await this.invokeAt(binaryPath, method, params);
|
|
74365
|
+
if (!isRecord3(result)) {
|
|
74366
|
+
throw new DeviceProviderError(
|
|
74367
|
+
"The native helper returned a non-object result",
|
|
74368
|
+
"INVALID_RESPONSE"
|
|
74369
|
+
);
|
|
74370
|
+
}
|
|
74371
|
+
return result;
|
|
74372
|
+
}
|
|
74373
|
+
async removeInstalledBinary() {
|
|
74374
|
+
const installed = await isExecutable(this.stableBinaryPath);
|
|
74375
|
+
if (!installed) return false;
|
|
74376
|
+
await rm(this.stableBinaryPath, { force: true });
|
|
74377
|
+
return true;
|
|
74378
|
+
}
|
|
74379
|
+
get stableBinaryPath() {
|
|
74380
|
+
return path19.join(this.homeDirectory, ".runtype", "device", "bin", "runtype-device");
|
|
74381
|
+
}
|
|
74382
|
+
installedCandidates() {
|
|
74383
|
+
return [
|
|
74384
|
+
this.stableBinaryPath,
|
|
74385
|
+
path19.join(
|
|
74386
|
+
this.homeDirectory,
|
|
74387
|
+
"Applications",
|
|
74388
|
+
"Runtype Device.app",
|
|
74389
|
+
"Contents",
|
|
74390
|
+
"MacOS",
|
|
74391
|
+
"runtype-device"
|
|
74392
|
+
),
|
|
74393
|
+
"/Applications/Runtype Device.app/Contents/MacOS/runtype-device"
|
|
74394
|
+
];
|
|
74395
|
+
}
|
|
74396
|
+
developmentCandidates() {
|
|
74397
|
+
return [
|
|
74398
|
+
...this.environment.RUNTYPE_DEVICE_HELPER_PATH ? [path19.resolve(this.environment.RUNTYPE_DEVICE_HELPER_PATH)] : [],
|
|
74399
|
+
path19.join(
|
|
74400
|
+
this.workingDirectory,
|
|
74401
|
+
"apps",
|
|
74402
|
+
"device-daemon-macos",
|
|
74403
|
+
".build",
|
|
74404
|
+
"release",
|
|
74405
|
+
"runtype-device"
|
|
74406
|
+
),
|
|
74407
|
+
path19.join(
|
|
74408
|
+
this.workingDirectory,
|
|
74409
|
+
"apps",
|
|
74410
|
+
"device-daemon-macos",
|
|
74411
|
+
".build",
|
|
74412
|
+
"debug",
|
|
74413
|
+
"runtype-device"
|
|
74414
|
+
)
|
|
74415
|
+
];
|
|
74416
|
+
}
|
|
74417
|
+
async firstExecutable(candidates) {
|
|
74418
|
+
for (const candidate of candidates) {
|
|
74419
|
+
if (await isExecutable(candidate)) return candidate;
|
|
74420
|
+
}
|
|
74421
|
+
return null;
|
|
74422
|
+
}
|
|
74423
|
+
resolveInstalledBinary() {
|
|
74424
|
+
return this.firstExecutable(this.installedCandidates());
|
|
74425
|
+
}
|
|
74426
|
+
resolveDevelopmentSource() {
|
|
74427
|
+
return this.firstExecutable(this.developmentCandidates());
|
|
74428
|
+
}
|
|
74429
|
+
async resolveBinary() {
|
|
74430
|
+
return await this.resolveInstalledBinary() ?? await this.firstExecutable(this.developmentCandidates());
|
|
74431
|
+
}
|
|
74432
|
+
assertPlatform() {
|
|
74433
|
+
const reason = this.unsupportedReason();
|
|
74434
|
+
if (!reason) return;
|
|
74435
|
+
throw new DeviceProviderError(
|
|
74436
|
+
reason,
|
|
74437
|
+
this.platform === "darwin" ? "UNSUPPORTED_OS_VERSION" : "UNSUPPORTED_PLATFORM"
|
|
74438
|
+
);
|
|
74439
|
+
}
|
|
74440
|
+
unsupportedReason() {
|
|
74441
|
+
if (this.platform !== "darwin") {
|
|
74442
|
+
return `Runtype Device currently supports macOS only (detected ${this.platform}/${this.architecture})`;
|
|
74443
|
+
}
|
|
74444
|
+
const darwinMajor = Number.parseInt(this.operatingSystemRelease.split(".")[0] ?? "", 10);
|
|
74445
|
+
if (!Number.isInteger(darwinMajor) || darwinMajor < 23) {
|
|
74446
|
+
return `Runtype Device requires macOS 14 or newer (detected Darwin ${this.operatingSystemRelease})`;
|
|
74447
|
+
}
|
|
74448
|
+
return null;
|
|
74449
|
+
}
|
|
74450
|
+
assertCompatible(hello) {
|
|
74451
|
+
if (hello.providerId !== this.id || hello.platform !== "macos") {
|
|
74452
|
+
throw new DeviceProviderError(
|
|
74453
|
+
`Unexpected native provider identity: ${hello.providerId}/${hello.platform}`,
|
|
74454
|
+
"PROVIDER_MISMATCH"
|
|
74455
|
+
);
|
|
74456
|
+
}
|
|
74457
|
+
if (hello.protocolVersion !== DEVICE_SIDECAR_PROTOCOL_VERSION) {
|
|
74458
|
+
throw new DeviceProviderError(
|
|
74459
|
+
`Native helper protocol ${hello.protocolVersion} is incompatible with CLI protocol ${DEVICE_SIDECAR_PROTOCOL_VERSION}`,
|
|
74460
|
+
"PROTOCOL_MISMATCH"
|
|
74461
|
+
);
|
|
74462
|
+
}
|
|
74463
|
+
}
|
|
74464
|
+
async describeInstallation(binaryPath, installed) {
|
|
74465
|
+
const hello = parseHello(await this.invokeAt(binaryPath, "hello"));
|
|
74466
|
+
this.assertCompatible(hello);
|
|
74467
|
+
return {
|
|
74468
|
+
providerId: this.id,
|
|
74469
|
+
binaryPath,
|
|
74470
|
+
helperVersion: hello.helperVersion,
|
|
74471
|
+
protocolVersion: hello.protocolVersion,
|
|
74472
|
+
installed
|
|
74473
|
+
};
|
|
74474
|
+
}
|
|
74475
|
+
async invokeAt(binaryPath, method, params = {}) {
|
|
74476
|
+
const id = randomUUID2();
|
|
74477
|
+
const request = JSON.stringify({
|
|
74478
|
+
protocolVersion: DEVICE_SIDECAR_PROTOCOL_VERSION,
|
|
74479
|
+
id,
|
|
74480
|
+
method,
|
|
74481
|
+
params
|
|
74482
|
+
});
|
|
74483
|
+
const child = spawn3(binaryPath, ["control"], {
|
|
74484
|
+
env: this.environment,
|
|
74485
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
74486
|
+
shell: false
|
|
74487
|
+
});
|
|
74488
|
+
const stdout = [];
|
|
74489
|
+
const stderr = [];
|
|
74490
|
+
let stdoutBytes = 0;
|
|
74491
|
+
let stderrBytes = 0;
|
|
74492
|
+
let outputExceeded = false;
|
|
74493
|
+
let timedOut = false;
|
|
74494
|
+
child.stdout.on("data", (chunk) => {
|
|
74495
|
+
stdoutBytes += chunk.length;
|
|
74496
|
+
if (stdoutBytes > MAX_SIDECAR_OUTPUT_BYTES) {
|
|
74497
|
+
outputExceeded = true;
|
|
74498
|
+
child.kill("SIGKILL");
|
|
74499
|
+
return;
|
|
74500
|
+
}
|
|
74501
|
+
stdout.push(chunk);
|
|
74502
|
+
});
|
|
74503
|
+
child.stderr.on("data", (chunk) => {
|
|
74504
|
+
if (stderrBytes >= MAX_SIDECAR_OUTPUT_BYTES) return;
|
|
74505
|
+
stderrBytes += chunk.length;
|
|
74506
|
+
stderr.push(chunk.subarray(0, MAX_SIDECAR_OUTPUT_BYTES - (stderrBytes - chunk.length)));
|
|
74507
|
+
});
|
|
74508
|
+
const completion = new Promise(
|
|
74509
|
+
(resolve13, reject) => {
|
|
74510
|
+
child.once("error", reject);
|
|
74511
|
+
child.once("close", (code, signal) => resolve13({ code, signal }));
|
|
74512
|
+
}
|
|
74513
|
+
);
|
|
74514
|
+
const timeout = setTimeout(() => {
|
|
74515
|
+
timedOut = true;
|
|
74516
|
+
child.kill("SIGKILL");
|
|
74517
|
+
}, this.sidecarTimeoutMs);
|
|
74518
|
+
child.stdin.on("error", () => {
|
|
74519
|
+
});
|
|
74520
|
+
child.stdin.end(`${request}
|
|
74521
|
+
`);
|
|
74522
|
+
let completionResult;
|
|
74523
|
+
try {
|
|
74524
|
+
completionResult = await completion;
|
|
74525
|
+
} catch (error51) {
|
|
74526
|
+
throw new DeviceProviderError(
|
|
74527
|
+
`Unable to start the native device helper at ${binaryPath}`,
|
|
74528
|
+
"HELPER_START_FAILED",
|
|
74529
|
+
false,
|
|
74530
|
+
{ cause: error51 }
|
|
74531
|
+
);
|
|
74532
|
+
} finally {
|
|
74533
|
+
clearTimeout(timeout);
|
|
74534
|
+
}
|
|
74535
|
+
if (timedOut) {
|
|
74536
|
+
throw new DeviceProviderError(
|
|
74537
|
+
`Native device helper timed out after ${this.sidecarTimeoutMs}ms`,
|
|
74538
|
+
"HELPER_TIMEOUT",
|
|
74539
|
+
true
|
|
74540
|
+
);
|
|
74541
|
+
}
|
|
74542
|
+
if (outputExceeded) {
|
|
74543
|
+
throw new DeviceProviderError(
|
|
74544
|
+
`Native device helper output exceeded ${MAX_SIDECAR_OUTPUT_BYTES} bytes`,
|
|
74545
|
+
"OUTPUT_TOO_LARGE"
|
|
74546
|
+
);
|
|
74547
|
+
}
|
|
74548
|
+
const output = Buffer.concat(stdout).toString("utf8").trim();
|
|
74549
|
+
let response;
|
|
74550
|
+
try {
|
|
74551
|
+
response = JSON.parse(output);
|
|
74552
|
+
} catch (error51) {
|
|
74553
|
+
const detail = Buffer.concat(stderr).toString("utf8").trim();
|
|
74554
|
+
throw new DeviceProviderError(
|
|
74555
|
+
`Native device helper returned invalid JSON${detail ? `: ${detail}` : ""}`,
|
|
74556
|
+
"INVALID_RESPONSE",
|
|
74557
|
+
false,
|
|
74558
|
+
{ cause: error51 }
|
|
74559
|
+
);
|
|
74560
|
+
}
|
|
74561
|
+
if (response.protocolVersion !== DEVICE_SIDECAR_PROTOCOL_VERSION || response.id !== id || typeof response.ok !== "boolean") {
|
|
74562
|
+
throw new DeviceProviderError(
|
|
74563
|
+
"Native device helper returned a mismatched response envelope",
|
|
74564
|
+
"INVALID_RESPONSE"
|
|
74565
|
+
);
|
|
74566
|
+
}
|
|
74567
|
+
if (!response.ok) {
|
|
74568
|
+
throw new DeviceProviderError(
|
|
74569
|
+
response.error?.message || "Native device operation failed",
|
|
74570
|
+
response.error?.code || "OPERATION_FAILED",
|
|
74571
|
+
response.error?.retryable === true
|
|
74572
|
+
);
|
|
74573
|
+
}
|
|
74574
|
+
if (completionResult.code !== 0) {
|
|
74575
|
+
throw new DeviceProviderError(
|
|
74576
|
+
`Native device helper exited with status ${completionResult.code ?? completionResult.signal ?? "unknown"}`,
|
|
74577
|
+
"HELPER_EXITED"
|
|
74578
|
+
);
|
|
74579
|
+
}
|
|
74580
|
+
return response.result;
|
|
74581
|
+
}
|
|
74582
|
+
};
|
|
74583
|
+
|
|
74584
|
+
// src/commands/devices.ts
|
|
74585
|
+
var defaultDependencies2 = {
|
|
74586
|
+
ensureAuth,
|
|
74587
|
+
createClient: (apiKey) => createCliClient(apiKey),
|
|
74588
|
+
createProvider: () => new MacOSDeviceProvider(),
|
|
74589
|
+
getApiUrl,
|
|
74590
|
+
sleep: (milliseconds) => new Promise((resolve13) => setTimeout(resolve13, milliseconds)),
|
|
74591
|
+
setExitCode: (code) => {
|
|
74592
|
+
process.exitCode = code;
|
|
74593
|
+
}
|
|
74594
|
+
};
|
|
74595
|
+
function isRecord4(value) {
|
|
74596
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74597
|
+
}
|
|
74598
|
+
function unwrapData(response) {
|
|
74599
|
+
if (!isRecord4(response) || !("data" in response)) {
|
|
74600
|
+
throw new Error("Runtype API returned an invalid device response");
|
|
74601
|
+
}
|
|
74602
|
+
return response.data;
|
|
74603
|
+
}
|
|
74604
|
+
function errorMessage2(error51) {
|
|
74605
|
+
return error51 instanceof Error ? error51.message : String(error51);
|
|
74606
|
+
}
|
|
74607
|
+
function apiStatusCode(error51) {
|
|
74608
|
+
return isRecord4(error51) && typeof error51.statusCode === "number" ? error51.statusCode : null;
|
|
74609
|
+
}
|
|
74610
|
+
function isRecordedRevocationError(error51) {
|
|
74611
|
+
if (apiStatusCode(error51) !== 503) return false;
|
|
74612
|
+
const data = isRecord4(error51) && isRecord4(error51.data) ? error51.data : null;
|
|
74613
|
+
const detail = typeof data?.error === "string" ? data.error : errorMessage2(error51);
|
|
74614
|
+
return detail.toLowerCase().includes("revocation was recorded");
|
|
74615
|
+
}
|
|
74616
|
+
function printCommandError(label, error51) {
|
|
74617
|
+
console.error(chalk46.red(label));
|
|
74618
|
+
console.error(chalk46.red(errorMessage2(error51)));
|
|
74619
|
+
}
|
|
74620
|
+
function printDevice(device) {
|
|
74621
|
+
printDetail("Runtype Device", [
|
|
74622
|
+
{ label: "ID", value: device.id },
|
|
74623
|
+
{ label: "Name", value: device.name },
|
|
74624
|
+
{ label: "Platform", value: device.platform },
|
|
74625
|
+
{ label: "Online", value: device.status?.online },
|
|
74626
|
+
{ label: "Last seen", value: device.status?.lastSeenAt },
|
|
74627
|
+
{ label: "Available tools", value: device.status?.availableTools.join(", ") },
|
|
74628
|
+
{ label: "MCP URL", value: device.mcpUrl },
|
|
74629
|
+
{ label: "Revoked", value: device.revokedAt }
|
|
74630
|
+
]);
|
|
74631
|
+
}
|
|
74632
|
+
function localDeviceId(status) {
|
|
74633
|
+
return typeof status.deviceId === "string" && status.deviceId.length > 0 ? status.deviceId : null;
|
|
74634
|
+
}
|
|
74635
|
+
async function authenticatedClient(dependencies) {
|
|
74636
|
+
const apiKey = await dependencies.ensureAuth({ exitOnFailure: false });
|
|
74637
|
+
if (!apiKey) {
|
|
74638
|
+
dependencies.setExitCode(1);
|
|
74639
|
+
return null;
|
|
74640
|
+
}
|
|
74641
|
+
return dependencies.createClient(apiKey);
|
|
74642
|
+
}
|
|
74643
|
+
async function waitForDevice(client, deviceId, dependencies) {
|
|
74644
|
+
let latest = null;
|
|
74645
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
74646
|
+
try {
|
|
74647
|
+
latest = unwrapData(
|
|
74648
|
+
await client.get(`/devices/${encodeURIComponent(deviceId)}`)
|
|
74649
|
+
);
|
|
74650
|
+
if (latest.status?.online) return latest;
|
|
74651
|
+
} catch {
|
|
74652
|
+
}
|
|
74653
|
+
if (attempt < 7) await dependencies.sleep(750);
|
|
74654
|
+
}
|
|
74655
|
+
return latest;
|
|
74656
|
+
}
|
|
74657
|
+
async function runTail2(logPath, lines) {
|
|
74658
|
+
const child = spawn4("/usr/bin/tail", ["-n", String(lines), "-f", logPath], {
|
|
74659
|
+
stdio: "inherit",
|
|
74660
|
+
shell: false
|
|
74661
|
+
});
|
|
74662
|
+
return new Promise((resolve13, reject) => {
|
|
74663
|
+
child.once("error", reject);
|
|
74664
|
+
child.once("close", (code) => resolve13(code ?? 1));
|
|
74665
|
+
});
|
|
74666
|
+
}
|
|
74667
|
+
function createDevicesCommand(overrides = {}) {
|
|
74668
|
+
const dependencies = { ...defaultDependencies2, ...overrides };
|
|
74669
|
+
const command = new Command37("devices").description(
|
|
74670
|
+
"Manage local computers connected to Runtype agents"
|
|
74671
|
+
);
|
|
74672
|
+
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) => {
|
|
74673
|
+
const provider = dependencies.createProvider();
|
|
74674
|
+
let pairedDeviceId = null;
|
|
74675
|
+
try {
|
|
74676
|
+
const installation = await provider.ensureInstalled({ helperPath: options.helperPath });
|
|
74677
|
+
const before = await provider.invoke("status");
|
|
74678
|
+
const existingDeviceId = localDeviceId(before);
|
|
74679
|
+
if (existingDeviceId) {
|
|
74680
|
+
throw new DeviceProviderError(
|
|
74681
|
+
`This Mac is already paired as ${existingDeviceId}. Run \`runtype devices status\` instead.`,
|
|
74682
|
+
"ALREADY_PAIRED"
|
|
74683
|
+
);
|
|
74684
|
+
}
|
|
74685
|
+
const client = await authenticatedClient(dependencies);
|
|
74686
|
+
if (!client) return;
|
|
74687
|
+
const pairing = unwrapData(
|
|
74688
|
+
await client.post("/devices/pairing-sessions", {})
|
|
74689
|
+
);
|
|
74690
|
+
const pairParams = {
|
|
74691
|
+
pairingUri: pairing.pairingUri,
|
|
74692
|
+
apiUrl: dependencies.getApiUrl()
|
|
74693
|
+
};
|
|
74694
|
+
if (options.name) pairParams.name = options.name;
|
|
74695
|
+
const paired = await provider.invoke("pair", pairParams);
|
|
74696
|
+
pairedDeviceId = paired.deviceId;
|
|
74697
|
+
const service = await provider.invoke("install");
|
|
74698
|
+
const local = await provider.invoke("status");
|
|
74699
|
+
const device = await waitForDevice(client, paired.deviceId, dependencies);
|
|
74700
|
+
const result = {
|
|
74701
|
+
deviceId: paired.deviceId,
|
|
74702
|
+
name: paired.name,
|
|
74703
|
+
online: device?.status?.online ?? false,
|
|
74704
|
+
mcpUrl: device?.mcpUrl ?? null,
|
|
74705
|
+
installation,
|
|
74706
|
+
service,
|
|
74707
|
+
local
|
|
74708
|
+
};
|
|
74709
|
+
if (options.json) {
|
|
74710
|
+
printJson(result);
|
|
74711
|
+
return;
|
|
74712
|
+
}
|
|
74713
|
+
console.log(chalk46.green(`Paired and started ${paired.name}`));
|
|
74714
|
+
console.log(` Device ID: ${chalk46.green(paired.deviceId)}`);
|
|
74715
|
+
console.log(
|
|
74716
|
+
` Relay: ${device?.status?.online ? chalk46.green("online") : chalk46.yellow("connecting")}`
|
|
74717
|
+
);
|
|
74718
|
+
if (device?.mcpUrl) console.log(` MCP URL: ${device.mcpUrl}`);
|
|
74719
|
+
if (local.messagesDatabaseReadable !== true) {
|
|
74720
|
+
console.log(
|
|
74721
|
+
chalk46.yellow(
|
|
74722
|
+
`
|
|
74723
|
+
Grant Full Disk Access to ${String(local.executablePath)} to enable iMessage reads.`
|
|
74724
|
+
)
|
|
74725
|
+
);
|
|
74726
|
+
}
|
|
74727
|
+
console.log(chalk46.gray("iMessage sending remains disabled by local policy."));
|
|
74728
|
+
} catch (error51) {
|
|
74729
|
+
printCommandError("Failed to set up Runtype Device", error51);
|
|
74730
|
+
if (pairedDeviceId) {
|
|
74731
|
+
console.error(
|
|
74732
|
+
chalk46.yellow(
|
|
74733
|
+
`Pairing completed as ${pairedDeviceId}, but startup did not finish. Run \`runtype devices install\` after correcting the error.`
|
|
74734
|
+
)
|
|
74735
|
+
);
|
|
74736
|
+
}
|
|
74737
|
+
dependencies.setExitCode(1);
|
|
74738
|
+
}
|
|
74739
|
+
});
|
|
74740
|
+
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) => {
|
|
74741
|
+
const provider = dependencies.createProvider();
|
|
74742
|
+
try {
|
|
74743
|
+
const installation = await provider.ensureInstalled({ helperPath: options.helperPath });
|
|
74744
|
+
const service = await provider.invoke("install");
|
|
74745
|
+
if (options.json) {
|
|
74746
|
+
printJson({ installation, service });
|
|
74747
|
+
} else {
|
|
74748
|
+
console.log(chalk46.green("Runtype Device is installed and running"));
|
|
74749
|
+
console.log(` Helper: ${installation.binaryPath}`);
|
|
74750
|
+
if (typeof service.logPath === "string") console.log(` Logs: ${service.logPath}`);
|
|
74751
|
+
}
|
|
74752
|
+
} catch (error51) {
|
|
74753
|
+
printCommandError("Failed to install Runtype Device", error51);
|
|
74754
|
+
dependencies.setExitCode(1);
|
|
74755
|
+
}
|
|
74756
|
+
});
|
|
74757
|
+
command.command("list").description("List paired devices and relay status").option("--json", "Output as JSON").action(async (options) => {
|
|
74758
|
+
try {
|
|
74759
|
+
const client = await authenticatedClient(dependencies);
|
|
74760
|
+
if (!client) return;
|
|
74761
|
+
const devices = unwrapData(await client.get("/devices"));
|
|
74762
|
+
if (options.json) {
|
|
74763
|
+
printJson({ data: devices });
|
|
74764
|
+
return;
|
|
74765
|
+
}
|
|
74766
|
+
if (devices.length === 0) {
|
|
74767
|
+
console.log(chalk46.gray("No paired devices"));
|
|
74768
|
+
return;
|
|
74769
|
+
}
|
|
74770
|
+
console.log(chalk46.cyan("Runtype Devices:"));
|
|
74771
|
+
for (const device of devices) {
|
|
74772
|
+
const state = device.revokedAt ? "revoked" : device.status?.online ? "online" : "offline";
|
|
74773
|
+
console.log(` ${chalk46.green(device.id)} ${device.name} ${state}`);
|
|
74774
|
+
}
|
|
74775
|
+
} catch (error51) {
|
|
74776
|
+
printCommandError("Failed to list Runtype devices", error51);
|
|
74777
|
+
dependencies.setExitCode(1);
|
|
74778
|
+
}
|
|
74779
|
+
});
|
|
74780
|
+
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) => {
|
|
74781
|
+
try {
|
|
74782
|
+
if (id) {
|
|
74783
|
+
const client = await authenticatedClient(dependencies);
|
|
74784
|
+
if (!client) return;
|
|
74785
|
+
const device = unwrapData(
|
|
74786
|
+
await client.get(`/devices/${encodeURIComponent(id)}`)
|
|
74787
|
+
);
|
|
74788
|
+
if (options.json) printJson(device);
|
|
74789
|
+
else printDevice(device);
|
|
74790
|
+
return;
|
|
74791
|
+
}
|
|
74792
|
+
const local = await dependencies.createProvider().invoke("status");
|
|
74793
|
+
if (options.json) {
|
|
74794
|
+
printJson(local);
|
|
74795
|
+
} else {
|
|
74796
|
+
printDetail("Local Runtype Device", [
|
|
74797
|
+
{ label: "Device ID", value: local.deviceId ?? "not paired" },
|
|
74798
|
+
{ label: "API URL", value: local.apiUrl },
|
|
74799
|
+
{ label: "Helper version", value: local.helperVersion },
|
|
74800
|
+
{ label: "Service installed", value: local.serviceInstalled },
|
|
74801
|
+
{ label: "Service running", value: local.serviceRunning },
|
|
74802
|
+
{ label: "Messages readable", value: local.messagesDatabaseReadable },
|
|
74803
|
+
{ label: "Sending enabled", value: local.allowSend },
|
|
74804
|
+
{ label: "Executable", value: local.executablePath },
|
|
74805
|
+
{ label: "Logs", value: local.logPath }
|
|
74806
|
+
]);
|
|
74807
|
+
}
|
|
74808
|
+
} catch (error51) {
|
|
74809
|
+
printCommandError("Failed to read Runtype Device status", error51);
|
|
74810
|
+
dependencies.setExitCode(1);
|
|
74811
|
+
}
|
|
74812
|
+
});
|
|
74813
|
+
command.command("doctor").description("Check native helper, background service, and Messages permissions").option("--json", "Output as JSON").action(async (options) => {
|
|
74814
|
+
const provider = dependencies.createProvider();
|
|
74815
|
+
try {
|
|
74816
|
+
const probe = await provider.probe();
|
|
74817
|
+
const local = probe.installed && !probe.reason ? await provider.invoke("doctor") : null;
|
|
74818
|
+
const healthy = probe.supported && probe.installed && local?.serviceRunning === true && local.messagesDatabaseReadable === true;
|
|
74819
|
+
const result = { healthy, probe, local };
|
|
74820
|
+
if (options.json) {
|
|
74821
|
+
printJson(result);
|
|
74822
|
+
} else {
|
|
74823
|
+
console.log(
|
|
74824
|
+
healthy ? chalk46.green("Runtype Device is healthy") : chalk46.yellow("Runtype Device needs attention")
|
|
74825
|
+
);
|
|
74826
|
+
console.log(` Platform supported: ${probe.supported ? "yes" : "no"}`);
|
|
74827
|
+
console.log(` Helper installed: ${probe.installed ? "yes" : "no"}`);
|
|
74828
|
+
if (probe.reason) console.log(` Helper: ${probe.reason}`);
|
|
74829
|
+
if (local) {
|
|
74830
|
+
console.log(` Service running: ${local.serviceRunning === true ? "yes" : "no"}`);
|
|
74831
|
+
console.log(
|
|
74832
|
+
` Messages readable: ${local.messagesDatabaseReadable === true ? "yes" : "no"}`
|
|
74833
|
+
);
|
|
74834
|
+
if (local.messagesDatabaseReadable !== true) {
|
|
74835
|
+
console.log(
|
|
74836
|
+
chalk46.yellow(` Grant Full Disk Access to ${String(local.executablePath)}`)
|
|
74837
|
+
);
|
|
74838
|
+
}
|
|
74839
|
+
}
|
|
74840
|
+
}
|
|
74841
|
+
if (!healthy) dependencies.setExitCode(1);
|
|
74842
|
+
} catch (error51) {
|
|
74843
|
+
printCommandError("Runtype Device diagnostics failed", error51);
|
|
74844
|
+
dependencies.setExitCode(1);
|
|
74845
|
+
}
|
|
74846
|
+
});
|
|
74847
|
+
command.command("policy <mode>").description("Set local iMessage send policy (allow-send or deny-send)").option("--json", "Output as JSON").action(async (mode, options) => {
|
|
74848
|
+
if (mode !== "allow-send" && mode !== "deny-send") {
|
|
74849
|
+
console.error(chalk46.red("Policy must be allow-send or deny-send"));
|
|
74850
|
+
dependencies.setExitCode(1);
|
|
74851
|
+
return;
|
|
74852
|
+
}
|
|
74853
|
+
const provider = dependencies.createProvider();
|
|
74854
|
+
let policyUpdated = false;
|
|
74855
|
+
try {
|
|
74856
|
+
const policy = await provider.invoke("policy", {
|
|
74857
|
+
allowSend: mode === "allow-send"
|
|
74858
|
+
});
|
|
74859
|
+
policyUpdated = true;
|
|
74860
|
+
const status = await provider.invoke("status");
|
|
74861
|
+
const service = status.serviceInstalled === true ? await provider.invoke("install") : null;
|
|
74862
|
+
const result = {
|
|
74863
|
+
...policy,
|
|
74864
|
+
capabilitiesRepublished: service !== null,
|
|
74865
|
+
service
|
|
74866
|
+
};
|
|
74867
|
+
if (options.json) printJson(result);
|
|
74868
|
+
else {
|
|
74869
|
+
console.log(
|
|
74870
|
+
chalk46.green(
|
|
74871
|
+
`Local iMessage sending is ${mode === "allow-send" ? "enabled" : "disabled"}`
|
|
74872
|
+
)
|
|
74873
|
+
);
|
|
74874
|
+
if (service)
|
|
74875
|
+
console.log(chalk46.gray("Restarted the device service to publish capabilities."));
|
|
74876
|
+
else {
|
|
74877
|
+
console.log(
|
|
74878
|
+
chalk46.yellow(
|
|
74879
|
+
"The device service is not installed; run `runtype devices install` to publish this policy."
|
|
74880
|
+
)
|
|
74881
|
+
);
|
|
74882
|
+
}
|
|
74883
|
+
}
|
|
74884
|
+
} catch (error51) {
|
|
74885
|
+
printCommandError(
|
|
74886
|
+
policyUpdated ? "Local policy was updated, but the device service could not publish it" : "Failed to update local device policy",
|
|
74887
|
+
error51
|
|
74888
|
+
);
|
|
74889
|
+
dependencies.setExitCode(1);
|
|
74890
|
+
}
|
|
74891
|
+
});
|
|
74892
|
+
command.command("mcp-url <id>").description("Print the managed MCP endpoint for a paired device").option("--json", "Output as JSON").action(async (id, options) => {
|
|
74893
|
+
try {
|
|
74894
|
+
const client = await authenticatedClient(dependencies);
|
|
74895
|
+
if (!client) return;
|
|
74896
|
+
const device = unwrapData(
|
|
74897
|
+
await client.get(`/devices/${encodeURIComponent(id)}`)
|
|
74898
|
+
);
|
|
74899
|
+
if (options.json) printJson({ deviceId: id, mcpUrl: device.mcpUrl ?? null });
|
|
74900
|
+
else if (device.mcpUrl) console.log(device.mcpUrl);
|
|
74901
|
+
else throw new Error("The API did not return an MCP URL for this device");
|
|
74902
|
+
} catch (error51) {
|
|
74903
|
+
printCommandError("Failed to get device MCP URL", error51);
|
|
74904
|
+
dependencies.setExitCode(1);
|
|
74905
|
+
}
|
|
74906
|
+
});
|
|
74907
|
+
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) => {
|
|
74908
|
+
if (!options.yes) {
|
|
74909
|
+
console.error(chalk46.red("Pass --yes to confirm irreversible device revocation"));
|
|
74910
|
+
dependencies.setExitCode(1);
|
|
74911
|
+
return;
|
|
74912
|
+
}
|
|
74913
|
+
try {
|
|
74914
|
+
const client = await authenticatedClient(dependencies);
|
|
74915
|
+
if (!client) return;
|
|
74916
|
+
const device = unwrapData(
|
|
74917
|
+
await client.post(`/devices/${encodeURIComponent(id)}/revoke`, {})
|
|
74918
|
+
);
|
|
74919
|
+
if (options.json) printJson(device);
|
|
74920
|
+
else {
|
|
74921
|
+
console.log(chalk46.green(`Revoked ${device.name} (${device.id})`));
|
|
74922
|
+
console.log(chalk46.gray("If this is the local Mac, run `runtype devices reset --yes`."));
|
|
74923
|
+
}
|
|
74924
|
+
} catch (error51) {
|
|
74925
|
+
if (isRecordedRevocationError(error51)) {
|
|
74926
|
+
const result = {
|
|
74927
|
+
deviceId: id,
|
|
74928
|
+
revoked: true,
|
|
74929
|
+
relayDisconnectConfirmed: false,
|
|
74930
|
+
message: errorMessage2(error51)
|
|
74931
|
+
};
|
|
74932
|
+
if (options.json) printJson(result);
|
|
74933
|
+
else {
|
|
74934
|
+
console.error(
|
|
74935
|
+
chalk46.yellow(
|
|
74936
|
+
`Revocation was recorded for ${id}, but relay disconnection was not confirmed.`
|
|
74937
|
+
)
|
|
74938
|
+
);
|
|
74939
|
+
console.error(
|
|
74940
|
+
chalk46.yellow("Retry this command, and reset the local Mac if applicable.")
|
|
74941
|
+
);
|
|
74942
|
+
}
|
|
74943
|
+
dependencies.setExitCode(1);
|
|
74944
|
+
return;
|
|
74945
|
+
}
|
|
74946
|
+
printCommandError("Failed to revoke Runtype device", error51);
|
|
74947
|
+
dependencies.setExitCode(1);
|
|
74948
|
+
}
|
|
74949
|
+
});
|
|
74950
|
+
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) => {
|
|
74951
|
+
try {
|
|
74952
|
+
const lines = Number.parseInt(options.lines, 10);
|
|
74953
|
+
if (!Number.isInteger(lines) || lines < 1 || lines > 1e4) {
|
|
74954
|
+
throw new Error("--lines must be an integer between 1 and 10000");
|
|
74955
|
+
}
|
|
74956
|
+
const status = await dependencies.createProvider().invoke("status");
|
|
74957
|
+
if (typeof status.logPath !== "string")
|
|
74958
|
+
throw new Error("The helper did not return a log path");
|
|
74959
|
+
if (options.follow) {
|
|
74960
|
+
const code = await runTail2(status.logPath, lines);
|
|
74961
|
+
if (code !== 0) dependencies.setExitCode(code);
|
|
74962
|
+
return;
|
|
74963
|
+
}
|
|
74964
|
+
const content = await readFile2(status.logPath, "utf8").catch((error51) => {
|
|
74965
|
+
const code = isRecord4(error51) ? error51.code : void 0;
|
|
74966
|
+
if (code === "ENOENT") return "";
|
|
74967
|
+
throw error51;
|
|
74968
|
+
});
|
|
74969
|
+
const entries = content.split(/\r?\n/u);
|
|
74970
|
+
if (entries.at(-1) === "") entries.pop();
|
|
74971
|
+
const selected = entries.slice(-lines).join("\n");
|
|
74972
|
+
if (selected) console.log(selected);
|
|
74973
|
+
else console.log(chalk46.gray("No local device logs yet"));
|
|
74974
|
+
} catch (error51) {
|
|
74975
|
+
printCommandError("Failed to read Runtype Device logs", error51);
|
|
74976
|
+
dependencies.setExitCode(1);
|
|
74977
|
+
}
|
|
74978
|
+
});
|
|
74979
|
+
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) => {
|
|
74980
|
+
const provider = dependencies.createProvider();
|
|
74981
|
+
try {
|
|
74982
|
+
const service = await provider.invoke("uninstall");
|
|
74983
|
+
const removedBinary = options.removeBinary ? await provider.removeInstalledBinary() : false;
|
|
74984
|
+
if (options.json) printJson({ service, removedBinary });
|
|
74985
|
+
else console.log(chalk46.green("Stopped and uninstalled the local Runtype Device service"));
|
|
74986
|
+
} catch (error51) {
|
|
74987
|
+
printCommandError("Failed to uninstall Runtype Device", error51);
|
|
74988
|
+
dependencies.setExitCode(1);
|
|
74989
|
+
}
|
|
74990
|
+
});
|
|
74991
|
+
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) => {
|
|
74992
|
+
if (!options.yes) {
|
|
74993
|
+
console.error(chalk46.red("Pass --yes to confirm destructive local reset"));
|
|
74994
|
+
dependencies.setExitCode(1);
|
|
74995
|
+
return;
|
|
74996
|
+
}
|
|
74997
|
+
try {
|
|
74998
|
+
const result = await dependencies.createProvider().invoke("reset", { rotateIdentity: true });
|
|
74999
|
+
if (options.json) printJson(result);
|
|
75000
|
+
else {
|
|
75001
|
+
console.log(chalk46.green("Cleared local device state and rotated the signing identity"));
|
|
75002
|
+
console.log(
|
|
75003
|
+
chalk46.gray("Revoke the old device in Runtype if it has not already been revoked.")
|
|
75004
|
+
);
|
|
75005
|
+
}
|
|
75006
|
+
} catch (error51) {
|
|
75007
|
+
printCommandError("Failed to reset local Runtype Device state", error51);
|
|
75008
|
+
dependencies.setExitCode(1);
|
|
75009
|
+
}
|
|
75010
|
+
});
|
|
75011
|
+
return command;
|
|
75012
|
+
}
|
|
75013
|
+
var devicesCommand = createDevicesCommand();
|
|
75014
|
+
|
|
74193
75015
|
// src/index.ts
|
|
74194
75016
|
init_credential_store();
|
|
74195
75017
|
|
|
@@ -74209,7 +75031,7 @@ function registerGlobalOptionForwarding(program2) {
|
|
|
74209
75031
|
// src/index.ts
|
|
74210
75032
|
loadEnv();
|
|
74211
75033
|
setCliTitle();
|
|
74212
|
-
var program = new
|
|
75034
|
+
var program = new Command38();
|
|
74213
75035
|
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
75036
|
program.addCommand(initCommand);
|
|
74215
75037
|
program.addCommand(loginCommand);
|
|
@@ -74247,6 +75069,7 @@ program.addCommand(skillsCommand);
|
|
|
74247
75069
|
program.addCommand(updateCommand);
|
|
74248
75070
|
program.addCommand(installMcpCommand);
|
|
74249
75071
|
program.addCommand(mcpCommand);
|
|
75072
|
+
program.addCommand(devicesCommand);
|
|
74250
75073
|
program.addCommand(appsCommand, { hidden: process.env.RUNTYPE_APPS_CLI !== "1" });
|
|
74251
75074
|
registerGlobalOptionForwarding(program);
|
|
74252
75075
|
program.exitOverride();
|
|
@@ -74279,17 +75102,17 @@ async function run() {
|
|
|
74279
75102
|
run().catch((error51) => {
|
|
74280
75103
|
const commanderError = error51;
|
|
74281
75104
|
if (commanderError.code === "commander.missingArgument") {
|
|
74282
|
-
console.error(
|
|
75105
|
+
console.error(chalk47.red(`Error: ${commanderError.message}`));
|
|
74283
75106
|
process.exit(1);
|
|
74284
75107
|
} else if (commanderError.code === "commander.unknownOption") {
|
|
74285
|
-
console.error(
|
|
75108
|
+
console.error(chalk47.red(`Error: ${commanderError.message}`));
|
|
74286
75109
|
process.exit(1);
|
|
74287
75110
|
} else if (commanderError.code === "commander.unknownCommand") {
|
|
74288
75111
|
process.exit(1);
|
|
74289
75112
|
} else if (commanderError.code === "commander.help" || commanderError.code === "commander.helpDisplayed" || commanderError.code === "commander.version") {
|
|
74290
75113
|
process.exit(0);
|
|
74291
75114
|
} else {
|
|
74292
|
-
console.error(
|
|
75115
|
+
console.error(chalk47.red("An unexpected error occurred:"));
|
|
74293
75116
|
console.error(error51);
|
|
74294
75117
|
process.exit(1);
|
|
74295
75118
|
}
|
|
@@ -74298,12 +75121,12 @@ async function handleNoCommand() {
|
|
|
74298
75121
|
const store = new CredentialStore();
|
|
74299
75122
|
const hasCredentials = await store.hasCredentials();
|
|
74300
75123
|
if (!hasCredentials) {
|
|
74301
|
-
console.log(
|
|
75124
|
+
console.log(chalk47.cyan("\nWelcome to Runtype CLI!\n"));
|
|
74302
75125
|
console.log("It looks like this is your first time. Run the setup wizard:");
|
|
74303
|
-
console.log(` ${
|
|
75126
|
+
console.log(` ${chalk47.green("runtype init")}
|
|
74304
75127
|
`);
|
|
74305
75128
|
console.log("Or see all available commands:");
|
|
74306
|
-
console.log(` ${
|
|
75129
|
+
console.log(` ${chalk47.green("runtype --help")}
|
|
74307
75130
|
`);
|
|
74308
75131
|
} else {
|
|
74309
75132
|
try {
|