@runtypelabs/cli 2.32.0 → 2.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +481 -281
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -15395,7 +15395,7 @@ function renderDefaultModelStrengthsGuide() {
|
|
|
15395
15395
|
).join("\n");
|
|
15396
15396
|
}
|
|
15397
15397
|
|
|
15398
|
-
// ../shared/dist/chunk-
|
|
15398
|
+
// ../shared/dist/chunk-UB6BK3TZ.mjs
|
|
15399
15399
|
function getNestedValue(obj, path20) {
|
|
15400
15400
|
let normalizedPath = path20;
|
|
15401
15401
|
normalizedPath = normalizedPath.replace(/^\$\.?/, "");
|
|
@@ -17017,6 +17017,353 @@ var artifactSSEEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
17017
17017
|
]);
|
|
17018
17018
|
var UNIFIED_SSE_EVENT_TYPE_SET = new Set(UNIFIED_SSE_EVENT_TYPES);
|
|
17019
17019
|
|
|
17020
|
+
// ../shared/dist/chunk-REUID6D4.mjs
|
|
17021
|
+
function parseHTMLWithRegex(html) {
|
|
17022
|
+
if (typeof html !== "string") {
|
|
17023
|
+
throw new Error("parseHTML: input must be a string");
|
|
17024
|
+
}
|
|
17025
|
+
try {
|
|
17026
|
+
const parser = {
|
|
17027
|
+
stripTags(value) {
|
|
17028
|
+
return value.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
17029
|
+
},
|
|
17030
|
+
querySelector(selector) {
|
|
17031
|
+
try {
|
|
17032
|
+
const tag = selector.replace(/[^a-zA-Z0-9-]/g, "");
|
|
17033
|
+
const match = html.match(new RegExp("<" + tag + "[^>]*>(.*?)<\\/" + tag + ">", "is"));
|
|
17034
|
+
return match ? parser.stripTags(match[1]).trim() || null : null;
|
|
17035
|
+
} catch {
|
|
17036
|
+
return null;
|
|
17037
|
+
}
|
|
17038
|
+
},
|
|
17039
|
+
querySelectorAll(selector) {
|
|
17040
|
+
try {
|
|
17041
|
+
const tag = selector.replace(/[^a-zA-Z0-9-]/g, "");
|
|
17042
|
+
const regex = new RegExp("<" + tag + "[^>]*>(.*?)<\\/" + tag + ">", "gis");
|
|
17043
|
+
const results = [];
|
|
17044
|
+
let match;
|
|
17045
|
+
while ((match = regex.exec(html)) !== null) {
|
|
17046
|
+
const value = parser.stripTags(match[1]).trim();
|
|
17047
|
+
if (value) results.push(value);
|
|
17048
|
+
}
|
|
17049
|
+
return results;
|
|
17050
|
+
} catch {
|
|
17051
|
+
return [];
|
|
17052
|
+
}
|
|
17053
|
+
}
|
|
17054
|
+
};
|
|
17055
|
+
const titleMatch = html.match(/<title[^>]*>(.*?)<\/title>/is);
|
|
17056
|
+
const title = titleMatch ? parser.stripTags(titleMatch[1]).trim() || void 0 : void 0;
|
|
17057
|
+
const headingRegex = /<h[1-6][^>]*>(.*?)<\/h[1-6]>/gis;
|
|
17058
|
+
const headings = [];
|
|
17059
|
+
let headingMatch;
|
|
17060
|
+
while ((headingMatch = headingRegex.exec(html)) !== null) {
|
|
17061
|
+
const text2 = parser.stripTags(headingMatch[1]).trim();
|
|
17062
|
+
if (text2) headings.push(text2);
|
|
17063
|
+
}
|
|
17064
|
+
const linkRegex = /<a\s[^>]*href=["']([^"']*)["'][^>]*>(.*?)<\/a>/gis;
|
|
17065
|
+
const links = [];
|
|
17066
|
+
let linkMatch;
|
|
17067
|
+
while ((linkMatch = linkRegex.exec(html)) !== null) {
|
|
17068
|
+
const href = linkMatch[1];
|
|
17069
|
+
const text2 = parser.stripTags(linkMatch[2]).trim();
|
|
17070
|
+
if (href) links.push({ href, text: text2 });
|
|
17071
|
+
}
|
|
17072
|
+
const text = parser.stripTags(html);
|
|
17073
|
+
return {
|
|
17074
|
+
title,
|
|
17075
|
+
headings,
|
|
17076
|
+
links,
|
|
17077
|
+
text,
|
|
17078
|
+
querySelector: parser.querySelector,
|
|
17079
|
+
querySelectorAll: parser.querySelectorAll
|
|
17080
|
+
};
|
|
17081
|
+
} catch (error51) {
|
|
17082
|
+
throw new Error(
|
|
17083
|
+
`parseHTML failed: ${error51 instanceof Error ? error51.message : "Unknown error"}`,
|
|
17084
|
+
{
|
|
17085
|
+
cause: error51
|
|
17086
|
+
}
|
|
17087
|
+
);
|
|
17088
|
+
}
|
|
17089
|
+
}
|
|
17090
|
+
function createPortableHelpers(parseHTML) {
|
|
17091
|
+
return {
|
|
17092
|
+
parseHTML,
|
|
17093
|
+
/**
|
|
17094
|
+
* Extract email addresses from text
|
|
17095
|
+
* @param text Text to search for emails
|
|
17096
|
+
* @returns Array of found email addresses
|
|
17097
|
+
*/
|
|
17098
|
+
extractEmails(text) {
|
|
17099
|
+
if (typeof text !== "string") {
|
|
17100
|
+
return [];
|
|
17101
|
+
}
|
|
17102
|
+
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
|
17103
|
+
const matches = text.match(emailRegex);
|
|
17104
|
+
return matches ? [...new Set(matches)] : [];
|
|
17105
|
+
},
|
|
17106
|
+
/**
|
|
17107
|
+
* Extract URLs from text
|
|
17108
|
+
* @param text Text to search for URLs
|
|
17109
|
+
* @returns Array of found URLs
|
|
17110
|
+
*/
|
|
17111
|
+
extractURLs(text) {
|
|
17112
|
+
if (typeof text !== "string") {
|
|
17113
|
+
return [];
|
|
17114
|
+
}
|
|
17115
|
+
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/g;
|
|
17116
|
+
const matches = text.match(urlRegex);
|
|
17117
|
+
return matches ? [...new Set(matches)] : [];
|
|
17118
|
+
},
|
|
17119
|
+
/**
|
|
17120
|
+
* Extract phone numbers from text
|
|
17121
|
+
* @param text Text to search for phone numbers
|
|
17122
|
+
* @returns Array of found phone numbers
|
|
17123
|
+
*/
|
|
17124
|
+
extractPhoneNumbers(text) {
|
|
17125
|
+
if (typeof text !== "string") {
|
|
17126
|
+
return [];
|
|
17127
|
+
}
|
|
17128
|
+
const phoneRegex = /(?:\+?1[-.\s]?)?(?:\([0-9]{3}\)|[0-9]{3})[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}/g;
|
|
17129
|
+
const matches = text.match(phoneRegex);
|
|
17130
|
+
return matches ? [...new Set(matches)] : [];
|
|
17131
|
+
},
|
|
17132
|
+
/**
|
|
17133
|
+
* Truncate text to specified length with ellipsis
|
|
17134
|
+
* @param text Text to truncate
|
|
17135
|
+
* @param length Maximum length
|
|
17136
|
+
* @returns Truncated text
|
|
17137
|
+
*/
|
|
17138
|
+
truncate(text, length) {
|
|
17139
|
+
if (typeof text !== "string" || typeof length !== "number") {
|
|
17140
|
+
return text || "";
|
|
17141
|
+
}
|
|
17142
|
+
if (text.length <= length) {
|
|
17143
|
+
return text;
|
|
17144
|
+
}
|
|
17145
|
+
return text.substring(0, length).trim() + "...";
|
|
17146
|
+
},
|
|
17147
|
+
/**
|
|
17148
|
+
* Create URL-friendly slug from text
|
|
17149
|
+
* @param text Text to slugify
|
|
17150
|
+
* @returns URL-friendly slug
|
|
17151
|
+
*/
|
|
17152
|
+
slugify(text) {
|
|
17153
|
+
if (typeof text !== "string") {
|
|
17154
|
+
return "";
|
|
17155
|
+
}
|
|
17156
|
+
return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
17157
|
+
},
|
|
17158
|
+
/**
|
|
17159
|
+
* Convert Markdown to HTML (basic conversion)
|
|
17160
|
+
* @param markdown Markdown text
|
|
17161
|
+
* @returns HTML string
|
|
17162
|
+
*/
|
|
17163
|
+
markdown2html(markdown) {
|
|
17164
|
+
if (typeof markdown !== "string") {
|
|
17165
|
+
return "";
|
|
17166
|
+
}
|
|
17167
|
+
return markdown.replace(/^### (.*$)/gim, "<h3>$1</h3>").replace(/^## (.*$)/gim, "<h2>$1</h2>").replace(/^# (.*$)/gim, "<h1>$1</h1>").replace(/\*\*(.*?)\*\*/gim, "<strong>$1</strong>").replace(/__(.*?)__/gim, "<strong>$1</strong>").replace(/\*(.*?)\*/gim, "<em>$1</em>").replace(/_(.*?)_/gim, "<em>$1</em>").replace(/`(.*?)`/gim, "<code>$1</code>").replace(/\[([^\]]+)\]\(([^)]+)\)/gim, '<a href="$2">$1</a>').replace(/\n\n/gim, "</p><p>").replace(/\n/gim, "<br>").replace(/^(?!<[h|u|o|l])/gim, "<p>").replace(/$/gim, "</p>").replace(/<p><\/p>/gim, "").replace(/<p>(<h[1-6]>.*<\/h[1-6]>)<\/p>/gim, "$1");
|
|
17168
|
+
},
|
|
17169
|
+
/**
|
|
17170
|
+
* Convert HTML to Markdown (basic conversion)
|
|
17171
|
+
* @param html HTML string
|
|
17172
|
+
* @returns Markdown text
|
|
17173
|
+
*/
|
|
17174
|
+
html2markdown(html) {
|
|
17175
|
+
if (typeof html !== "string") {
|
|
17176
|
+
return "";
|
|
17177
|
+
}
|
|
17178
|
+
return html.replace(/<h1[^>]*>(.*?)<\/h1>/gim, "# $1\n\n").replace(/<h2[^>]*>(.*?)<\/h2>/gim, "## $1\n\n").replace(/<h3[^>]*>(.*?)<\/h3>/gim, "### $1\n\n").replace(/<h4[^>]*>(.*?)<\/h4>/gim, "#### $1\n\n").replace(/<h5[^>]*>(.*?)<\/h5>/gim, "##### $1\n\n").replace(/<h6[^>]*>(.*?)<\/h6>/gim, "###### $1\n\n").replace(/<strong[^>]*>(.*?)<\/strong>/gim, "**$1**").replace(/<b[^>]*>(.*?)<\/b>/gim, "**$1**").replace(/<em[^>]*>(.*?)<\/em>/gim, "*$1*").replace(/<i[^>]*>(.*?)<\/i>/gim, "*$1*").replace(/<code[^>]*>(.*?)<\/code>/gim, "`$1`").replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gim, "[$2]($1)").replace(/<br[^>]*>/gim, "\n").replace(/<\/p>/gim, "\n\n").replace(/<p[^>]*>/gim, "").replace(/<li[^>]*>(.*?)<\/li>/gim, "- $1\n").replace(/<ul[^>]*>/gim, "\n").replace(/<\/ul>/gim, "\n").replace(/<ol[^>]*>/gim, "\n").replace(/<\/ol>/gim, "\n").replace(/<[^>]+>/gim, "").replace(/\n\s*\n\s*\n/gim, "\n\n").trim();
|
|
17179
|
+
},
|
|
17180
|
+
/**
|
|
17181
|
+
* Parse date from various string formats
|
|
17182
|
+
* @param dateStr Date string to parse
|
|
17183
|
+
* @returns Date object or null if invalid
|
|
17184
|
+
*/
|
|
17185
|
+
parseDate(dateStr) {
|
|
17186
|
+
if (typeof dateStr !== "string") {
|
|
17187
|
+
return null;
|
|
17188
|
+
}
|
|
17189
|
+
try {
|
|
17190
|
+
const date5 = new Date(dateStr);
|
|
17191
|
+
return isNaN(date5.getTime()) ? null : date5;
|
|
17192
|
+
} catch {
|
|
17193
|
+
return null;
|
|
17194
|
+
}
|
|
17195
|
+
},
|
|
17196
|
+
/**
|
|
17197
|
+
* Format date according to specified format
|
|
17198
|
+
* @param date Date object or string
|
|
17199
|
+
* @param format Format string (simple patterns)
|
|
17200
|
+
* @returns Formatted date string
|
|
17201
|
+
*/
|
|
17202
|
+
formatDate(date5, format) {
|
|
17203
|
+
const defaultFormat = format || "YYYY-MM-DD";
|
|
17204
|
+
let dateObj;
|
|
17205
|
+
if (typeof date5 === "string") {
|
|
17206
|
+
dateObj = new Date(date5);
|
|
17207
|
+
} else if (date5 instanceof Date) {
|
|
17208
|
+
dateObj = date5;
|
|
17209
|
+
} else {
|
|
17210
|
+
return "";
|
|
17211
|
+
}
|
|
17212
|
+
if (isNaN(dateObj.getTime())) {
|
|
17213
|
+
return "";
|
|
17214
|
+
}
|
|
17215
|
+
const year = dateObj.getFullYear();
|
|
17216
|
+
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
|
|
17217
|
+
const day = String(dateObj.getDate()).padStart(2, "0");
|
|
17218
|
+
const hours = String(dateObj.getHours()).padStart(2, "0");
|
|
17219
|
+
const minutes = String(dateObj.getMinutes()).padStart(2, "0");
|
|
17220
|
+
const seconds = String(dateObj.getSeconds()).padStart(2, "0");
|
|
17221
|
+
return defaultFormat.replace(/YYYY/g, String(year)).replace(/MM/g, month).replace(/DD/g, day).replace(/HH/g, hours).replace(/mm/g, minutes).replace(/ss/g, seconds);
|
|
17222
|
+
},
|
|
17223
|
+
/**
|
|
17224
|
+
* Add days to a date
|
|
17225
|
+
* @param date Date object or string
|
|
17226
|
+
* @param days Number of days to add (can be negative)
|
|
17227
|
+
* @returns New Date object
|
|
17228
|
+
*/
|
|
17229
|
+
addDays(date5, days) {
|
|
17230
|
+
let dateObj;
|
|
17231
|
+
if (typeof date5 === "string") {
|
|
17232
|
+
dateObj = new Date(date5);
|
|
17233
|
+
} else if (date5 instanceof Date) {
|
|
17234
|
+
dateObj = new Date(date5.getTime());
|
|
17235
|
+
} else {
|
|
17236
|
+
return null;
|
|
17237
|
+
}
|
|
17238
|
+
if (isNaN(dateObj.getTime()) || typeof days !== "number") {
|
|
17239
|
+
return null;
|
|
17240
|
+
}
|
|
17241
|
+
dateObj.setDate(dateObj.getDate() + days);
|
|
17242
|
+
return dateObj;
|
|
17243
|
+
},
|
|
17244
|
+
/**
|
|
17245
|
+
* Validate email address format
|
|
17246
|
+
* @param email Email address to validate
|
|
17247
|
+
* @returns True if valid email format
|
|
17248
|
+
*/
|
|
17249
|
+
isValidEmail(email3) {
|
|
17250
|
+
if (typeof email3 !== "string") {
|
|
17251
|
+
return false;
|
|
17252
|
+
}
|
|
17253
|
+
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
17254
|
+
return emailRegex.test(email3);
|
|
17255
|
+
},
|
|
17256
|
+
/**
|
|
17257
|
+
* Validate URL format
|
|
17258
|
+
* @param url URL to validate
|
|
17259
|
+
* @returns True if valid URL format
|
|
17260
|
+
*/
|
|
17261
|
+
isValidURL(url2) {
|
|
17262
|
+
if (typeof url2 !== "string") {
|
|
17263
|
+
return false;
|
|
17264
|
+
}
|
|
17265
|
+
try {
|
|
17266
|
+
new URL(url2);
|
|
17267
|
+
return true;
|
|
17268
|
+
} catch {
|
|
17269
|
+
return false;
|
|
17270
|
+
}
|
|
17271
|
+
},
|
|
17272
|
+
/**
|
|
17273
|
+
* Validate JSON string
|
|
17274
|
+
* @param str String to validate as JSON
|
|
17275
|
+
* @returns True if valid JSON
|
|
17276
|
+
*/
|
|
17277
|
+
isValidJSON(str) {
|
|
17278
|
+
if (typeof str !== "string") {
|
|
17279
|
+
return false;
|
|
17280
|
+
}
|
|
17281
|
+
try {
|
|
17282
|
+
JSON.parse(str);
|
|
17283
|
+
return true;
|
|
17284
|
+
} catch {
|
|
17285
|
+
return false;
|
|
17286
|
+
}
|
|
17287
|
+
},
|
|
17288
|
+
/**
|
|
17289
|
+
* Encode string to base64
|
|
17290
|
+
* @param str String to encode
|
|
17291
|
+
* @returns Base64 encoded string
|
|
17292
|
+
*/
|
|
17293
|
+
base64Encode(str) {
|
|
17294
|
+
if (typeof str !== "string") {
|
|
17295
|
+
return "";
|
|
17296
|
+
}
|
|
17297
|
+
try {
|
|
17298
|
+
return btoa(str);
|
|
17299
|
+
} catch {
|
|
17300
|
+
return btoa(unescape(encodeURIComponent(str)));
|
|
17301
|
+
}
|
|
17302
|
+
},
|
|
17303
|
+
/**
|
|
17304
|
+
* Decode base64 string
|
|
17305
|
+
* @param str Base64 string to decode
|
|
17306
|
+
* @returns Decoded string
|
|
17307
|
+
*/
|
|
17308
|
+
base64Decode(str) {
|
|
17309
|
+
if (typeof str !== "string") {
|
|
17310
|
+
return "";
|
|
17311
|
+
}
|
|
17312
|
+
try {
|
|
17313
|
+
return atob(str);
|
|
17314
|
+
} catch {
|
|
17315
|
+
try {
|
|
17316
|
+
return decodeURIComponent(escape(atob(str)));
|
|
17317
|
+
} catch {
|
|
17318
|
+
return "";
|
|
17319
|
+
}
|
|
17320
|
+
}
|
|
17321
|
+
},
|
|
17322
|
+
/**
|
|
17323
|
+
* URL encode string
|
|
17324
|
+
* @param str String to encode
|
|
17325
|
+
* @returns URL encoded string
|
|
17326
|
+
*/
|
|
17327
|
+
urlEncode(str) {
|
|
17328
|
+
if (typeof str !== "string") {
|
|
17329
|
+
return "";
|
|
17330
|
+
}
|
|
17331
|
+
return encodeURIComponent(str);
|
|
17332
|
+
},
|
|
17333
|
+
/**
|
|
17334
|
+
* URL decode string
|
|
17335
|
+
* @param str URL encoded string to decode
|
|
17336
|
+
* @returns Decoded string
|
|
17337
|
+
*/
|
|
17338
|
+
urlDecode(str) {
|
|
17339
|
+
if (typeof str !== "string") {
|
|
17340
|
+
return "";
|
|
17341
|
+
}
|
|
17342
|
+
try {
|
|
17343
|
+
return decodeURIComponent(str);
|
|
17344
|
+
} catch {
|
|
17345
|
+
return str;
|
|
17346
|
+
}
|
|
17347
|
+
},
|
|
17348
|
+
/**
|
|
17349
|
+
* Return the first argument that is neither null nor undefined (a named
|
|
17350
|
+
* first-non-null / nullish coalesce). The script-side counterpart of the
|
|
17351
|
+
* `{{a ?? b}}` template operator — in templates prefer `{{a ?? b}}`; use
|
|
17352
|
+
* `helpers.coalesce(a, b)` inside transform-data scripts.
|
|
17353
|
+
* @param values Candidate values in priority order
|
|
17354
|
+
* @returns The first non-null/undefined value, or null if all are null/undefined
|
|
17355
|
+
*/
|
|
17356
|
+
coalesce(...values) {
|
|
17357
|
+
for (const value of values) {
|
|
17358
|
+
if (value !== null && value !== void 0) {
|
|
17359
|
+
return value;
|
|
17360
|
+
}
|
|
17361
|
+
}
|
|
17362
|
+
return null;
|
|
17363
|
+
}
|
|
17364
|
+
};
|
|
17365
|
+
}
|
|
17366
|
+
|
|
17020
17367
|
// ../shared/dist/index.mjs
|
|
17021
17368
|
var FORMULA_TRIGGER = /^[=+\-@\t\r\n]/;
|
|
17022
17369
|
var STRICT_NUMBER = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
|
|
@@ -17164,6 +17511,14 @@ var AGENT_CONTENT_CONFIG_KEYS = {
|
|
|
17164
17511
|
tenancyStrategy: true
|
|
17165
17512
|
};
|
|
17166
17513
|
var AGENT_CONTENT_CONFIG_KEY_LIST = Object.keys(AGENT_CONTENT_CONFIG_KEYS).sort();
|
|
17514
|
+
function stripTrailingChars(value, char) {
|
|
17515
|
+
const code = char.charCodeAt(0);
|
|
17516
|
+
let end = value.length;
|
|
17517
|
+
while (end > 0 && value.charCodeAt(end - 1) === code) {
|
|
17518
|
+
end--;
|
|
17519
|
+
}
|
|
17520
|
+
return end === value.length ? value : value.slice(0, end);
|
|
17521
|
+
}
|
|
17167
17522
|
var configurationStatusSchema = external_exports.enum(["configured", "needs_configuration", "invalid"]);
|
|
17168
17523
|
var secretDocsUrlSchema = external_exports.url({ protocol: /^https$/ }).max(2048);
|
|
17169
17524
|
var secretRequirementSourceSchema = external_exports.object({
|
|
@@ -22005,280 +22360,8 @@ function parseCreateFlowValidationInput(payload) {
|
|
|
22005
22360
|
};
|
|
22006
22361
|
}
|
|
22007
22362
|
var SERIALIZED_HELPERS_SOURCE = `
|
|
22008
|
-
// Transform helper functions
|
|
22009
|
-
const helpers = (
|
|
22010
|
-
// Regex-based HTML tag stripper for extracting text content
|
|
22011
|
-
const stripTags = (html) => html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()
|
|
22012
|
-
|
|
22013
|
-
return {
|
|
22014
|
-
parseHTML: (html) => {
|
|
22015
|
-
if (typeof html !== 'string') throw new Error('parseHTML: input must be a string')
|
|
22016
|
-
try {
|
|
22017
|
-
// Extract title
|
|
22018
|
-
const titleMatch = html.match(/<title[^>]*>(.*?)<\\/title>/is)
|
|
22019
|
-
const title = titleMatch ? stripTags(titleMatch[1]).trim() || undefined : undefined
|
|
22020
|
-
|
|
22021
|
-
// Extract headings (h1-h6)
|
|
22022
|
-
const headingRegex = /<h[1-6][^>]*>(.*?)<\\/h[1-6]>/gis
|
|
22023
|
-
const headings = []
|
|
22024
|
-
let hMatch
|
|
22025
|
-
while ((hMatch = headingRegex.exec(html)) !== null) {
|
|
22026
|
-
const text = stripTags(hMatch[1]).trim()
|
|
22027
|
-
if (text) headings.push(text)
|
|
22028
|
-
}
|
|
22029
|
-
|
|
22030
|
-
// Extract links
|
|
22031
|
-
const linkRegex = /<a\\s[^>]*href=["']([^"']*)["'][^>]*>(.*?)<\\/a>/gis
|
|
22032
|
-
const links = []
|
|
22033
|
-
let lMatch
|
|
22034
|
-
while ((lMatch = linkRegex.exec(html)) !== null) {
|
|
22035
|
-
const href = lMatch[1]
|
|
22036
|
-
const text = stripTags(lMatch[2]).trim()
|
|
22037
|
-
if (href) links.push({ href, text })
|
|
22038
|
-
}
|
|
22039
|
-
|
|
22040
|
-
// Extract all text content
|
|
22041
|
-
const text = stripTags(html)
|
|
22042
|
-
|
|
22043
|
-
// Simple querySelector (CSS element selectors only)
|
|
22044
|
-
const querySelector = (selector) => {
|
|
22045
|
-
try {
|
|
22046
|
-
const tag = selector.replace(/[^a-zA-Z0-9-]/g, '')
|
|
22047
|
-
const match = html.match(new RegExp('<' + tag + '[^>]*>(.*?)<\\\\/' + tag + '>', 'is'))
|
|
22048
|
-
return match ? stripTags(match[1]).trim() || null : null
|
|
22049
|
-
} catch (error) {
|
|
22050
|
-
return null
|
|
22051
|
-
}
|
|
22052
|
-
}
|
|
22053
|
-
|
|
22054
|
-
// Simple querySelectorAll (CSS element selectors only)
|
|
22055
|
-
const querySelectorAll = (selector) => {
|
|
22056
|
-
try {
|
|
22057
|
-
const tag = selector.replace(/[^a-zA-Z0-9-]/g, '')
|
|
22058
|
-
const regex = new RegExp('<' + tag + '[^>]*>(.*?)<\\\\/' + tag + '>', 'gis')
|
|
22059
|
-
const results = []
|
|
22060
|
-
let m
|
|
22061
|
-
while ((m = regex.exec(html)) !== null) {
|
|
22062
|
-
const t = stripTags(m[1]).trim()
|
|
22063
|
-
if (t) results.push(t)
|
|
22064
|
-
}
|
|
22065
|
-
return results
|
|
22066
|
-
} catch (error) {
|
|
22067
|
-
return []
|
|
22068
|
-
}
|
|
22069
|
-
}
|
|
22070
|
-
|
|
22071
|
-
return { title, headings, links, text, querySelector, querySelectorAll }
|
|
22072
|
-
} catch (error) {
|
|
22073
|
-
throw new Error('parseHTML failed: ' + (error instanceof Error ? error.message : 'Unknown error'))
|
|
22074
|
-
}
|
|
22075
|
-
},
|
|
22076
|
-
|
|
22077
|
-
extractEmails: (text) => {
|
|
22078
|
-
if (typeof text !== 'string') return []
|
|
22079
|
-
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g
|
|
22080
|
-
const matches = text.match(emailRegex)
|
|
22081
|
-
return matches ? [...new Set(matches)] : []
|
|
22082
|
-
},
|
|
22083
|
-
|
|
22084
|
-
extractURLs: (text) => {
|
|
22085
|
-
if (typeof text !== 'string') return []
|
|
22086
|
-
const urlRegex = /https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&/=]*)/g
|
|
22087
|
-
const matches = text.match(urlRegex)
|
|
22088
|
-
return matches ? [...new Set(matches)] : []
|
|
22089
|
-
},
|
|
22090
|
-
|
|
22091
|
-
extractPhoneNumbers: (text) => {
|
|
22092
|
-
if (typeof text !== 'string') return []
|
|
22093
|
-
const phoneRegex = /(?:\\+?1[-.\\s]?)?(?:\\([0-9]{3}\\)|[0-9]{3})[-.\\s]?[0-9]{3}[-.\\s]?[0-9]{4}/g
|
|
22094
|
-
const matches = text.match(phoneRegex)
|
|
22095
|
-
return matches ? [...new Set(matches)] : []
|
|
22096
|
-
},
|
|
22097
|
-
|
|
22098
|
-
truncate: (text, length) => {
|
|
22099
|
-
if (typeof text !== 'string' || typeof length !== 'number') return text || ''
|
|
22100
|
-
if (text.length <= length) return text
|
|
22101
|
-
return text.substring(0, length).trim() + '...'
|
|
22102
|
-
},
|
|
22103
|
-
|
|
22104
|
-
slugify: (text) => {
|
|
22105
|
-
if (typeof text !== 'string') return ''
|
|
22106
|
-
return text
|
|
22107
|
-
.toLowerCase()
|
|
22108
|
-
.trim()
|
|
22109
|
-
.replace(/[^\\w\\s-]/g, '')
|
|
22110
|
-
.replace(/[\\s_-]+/g, '-')
|
|
22111
|
-
.replace(/^-+|-+$/g, '')
|
|
22112
|
-
},
|
|
22113
|
-
|
|
22114
|
-
markdown2html: (markdown) => {
|
|
22115
|
-
if (typeof markdown !== 'string') return ''
|
|
22116
|
-
return markdown
|
|
22117
|
-
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
|
22118
|
-
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
|
22119
|
-
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
|
22120
|
-
.replace(/\\*\\*(.*?)\\*\\*/gim, '<strong>$1</strong>')
|
|
22121
|
-
.replace(/__(.*?)__/gim, '<strong>$1</strong>')
|
|
22122
|
-
.replace(/\\*(.*?)\\*/gim, '<em>$1</em>')
|
|
22123
|
-
.replace(/_(.*?)_/gim, '<em>$1</em>')
|
|
22124
|
-
.replace(/\`(.*?)\`/gim, '<code>$1</code>')
|
|
22125
|
-
.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/gim, '<a href="$2">$1</a>')
|
|
22126
|
-
.replace(/\\n\\n/gim, '</p><p>')
|
|
22127
|
-
.replace(/\\n/gim, '<br>')
|
|
22128
|
-
.replace(/^(?!<[h|u|o|l])/gim, '<p>')
|
|
22129
|
-
.replace(/$/gim, '</p>')
|
|
22130
|
-
.replace(/<p><\\/p>/gim, '')
|
|
22131
|
-
.replace(/<p>(<h[1-6]>.*<\\/h[1-6]>)<\\/p>/gim, '$1')
|
|
22132
|
-
},
|
|
22133
|
-
|
|
22134
|
-
html2markdown: (html) => {
|
|
22135
|
-
if (typeof html !== 'string') return ''
|
|
22136
|
-
return html
|
|
22137
|
-
.replace(/<h1[^>]*>(.*?)<\\/h1>/gim, '# $1\\n\\n')
|
|
22138
|
-
.replace(/<h2[^>]*>(.*?)<\\/h2>/gim, '## $1\\n\\n')
|
|
22139
|
-
.replace(/<h3[^>]*>(.*?)<\\/h3>/gim, '### $1\\n\\n')
|
|
22140
|
-
.replace(/<h4[^>]*>(.*?)<\\/h4>/gim, '#### $1\\n\\n')
|
|
22141
|
-
.replace(/<h5[^>]*>(.*?)<\\/h5>/gim, '##### $1\\n\\n')
|
|
22142
|
-
.replace(/<h6[^>]*>(.*?)<\\/h6>/gim, '###### $1\\n\\n')
|
|
22143
|
-
.replace(/<strong[^>]*>(.*?)<\\/strong>/gim, '**$1**')
|
|
22144
|
-
.replace(/<b[^>]*>(.*?)<\\/b>/gim, '**$1**')
|
|
22145
|
-
.replace(/<em[^>]*>(.*?)<\\/em>/gim, '*$1*')
|
|
22146
|
-
.replace(/<i[^>]*>(.*?)<\\/i>/gim, '*$1*')
|
|
22147
|
-
.replace(/<code[^>]*>(.*?)<\\/code>/gim, '\`$1\`')
|
|
22148
|
-
.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\\/a>/gim, '[$2]($1)')
|
|
22149
|
-
.replace(/<br[^>]*>/gim, '\\n')
|
|
22150
|
-
.replace(/<\\/p>/gim, '\\n\\n')
|
|
22151
|
-
.replace(/<p[^>]*>/gim, '')
|
|
22152
|
-
.replace(/<li[^>]*>(.*?)<\\/li>/gim, '- $1\\n')
|
|
22153
|
-
.replace(/<ul[^>]*>/gim, '\\n')
|
|
22154
|
-
.replace(/<\\/ul>/gim, '\\n')
|
|
22155
|
-
.replace(/<ol[^>]*>/gim, '\\n')
|
|
22156
|
-
.replace(/<\\/ol>/gim, '\\n')
|
|
22157
|
-
.replace(/<[^>]+>/gim, '')
|
|
22158
|
-
.replace(/\\n\\s*\\n\\s*\\n/gim, '\\n\\n')
|
|
22159
|
-
.trim()
|
|
22160
|
-
},
|
|
22161
|
-
|
|
22162
|
-
parseDate: (dateStr) => {
|
|
22163
|
-
if (typeof dateStr !== 'string') return null
|
|
22164
|
-
try {
|
|
22165
|
-
const date = new Date(dateStr)
|
|
22166
|
-
return isNaN(date.getTime()) ? null : date
|
|
22167
|
-
} catch {
|
|
22168
|
-
return null
|
|
22169
|
-
}
|
|
22170
|
-
},
|
|
22171
|
-
|
|
22172
|
-
formatDate: (date, format) => {
|
|
22173
|
-
const defaultFormat = format || 'YYYY-MM-DD'
|
|
22174
|
-
let dateObj
|
|
22175
|
-
if (typeof date === 'string') {
|
|
22176
|
-
dateObj = new Date(date)
|
|
22177
|
-
} else if (date instanceof Date) {
|
|
22178
|
-
dateObj = date
|
|
22179
|
-
} else {
|
|
22180
|
-
return ''
|
|
22181
|
-
}
|
|
22182
|
-
if (isNaN(dateObj.getTime())) return ''
|
|
22183
|
-
const year = dateObj.getFullYear()
|
|
22184
|
-
const month = String(dateObj.getMonth() + 1).padStart(2, '0')
|
|
22185
|
-
const day = String(dateObj.getDate()).padStart(2, '0')
|
|
22186
|
-
const hours = String(dateObj.getHours()).padStart(2, '0')
|
|
22187
|
-
const minutes = String(dateObj.getMinutes()).padStart(2, '0')
|
|
22188
|
-
const seconds = String(dateObj.getSeconds()).padStart(2, '0')
|
|
22189
|
-
return defaultFormat
|
|
22190
|
-
.replace(/YYYY/g, String(year))
|
|
22191
|
-
.replace(/MM/g, month)
|
|
22192
|
-
.replace(/DD/g, day)
|
|
22193
|
-
.replace(/HH/g, hours)
|
|
22194
|
-
.replace(/mm/g, minutes)
|
|
22195
|
-
.replace(/ss/g, seconds)
|
|
22196
|
-
},
|
|
22197
|
-
|
|
22198
|
-
addDays: (date, days) => {
|
|
22199
|
-
let dateObj
|
|
22200
|
-
if (typeof date === 'string') {
|
|
22201
|
-
dateObj = new Date(date)
|
|
22202
|
-
} else if (date instanceof Date) {
|
|
22203
|
-
dateObj = new Date(date.getTime())
|
|
22204
|
-
} else {
|
|
22205
|
-
return null
|
|
22206
|
-
}
|
|
22207
|
-
if (isNaN(dateObj.getTime()) || typeof days !== 'number') return null
|
|
22208
|
-
dateObj.setDate(dateObj.getDate() + days)
|
|
22209
|
-
return dateObj
|
|
22210
|
-
},
|
|
22211
|
-
|
|
22212
|
-
isValidEmail: (email) => {
|
|
22213
|
-
if (typeof email !== 'string') return false
|
|
22214
|
-
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/
|
|
22215
|
-
return emailRegex.test(email)
|
|
22216
|
-
},
|
|
22217
|
-
|
|
22218
|
-
isValidURL: (url) => {
|
|
22219
|
-
if (typeof url !== 'string') return false
|
|
22220
|
-
try {
|
|
22221
|
-
new URL(url)
|
|
22222
|
-
return true
|
|
22223
|
-
} catch {
|
|
22224
|
-
return false
|
|
22225
|
-
}
|
|
22226
|
-
},
|
|
22227
|
-
|
|
22228
|
-
isValidJSON: (str) => {
|
|
22229
|
-
if (typeof str !== 'string') return false
|
|
22230
|
-
try {
|
|
22231
|
-
JSON.parse(str)
|
|
22232
|
-
return true
|
|
22233
|
-
} catch {
|
|
22234
|
-
return false
|
|
22235
|
-
}
|
|
22236
|
-
},
|
|
22237
|
-
|
|
22238
|
-
base64Encode: (str) => {
|
|
22239
|
-
if (typeof str !== 'string') return ''
|
|
22240
|
-
try {
|
|
22241
|
-
return btoa(str)
|
|
22242
|
-
} catch {
|
|
22243
|
-
return btoa(unescape(encodeURIComponent(str)))
|
|
22244
|
-
}
|
|
22245
|
-
},
|
|
22246
|
-
|
|
22247
|
-
base64Decode: (str) => {
|
|
22248
|
-
if (typeof str !== 'string') return ''
|
|
22249
|
-
try {
|
|
22250
|
-
return atob(str)
|
|
22251
|
-
} catch {
|
|
22252
|
-
try {
|
|
22253
|
-
return decodeURIComponent(escape(atob(str)))
|
|
22254
|
-
} catch {
|
|
22255
|
-
return ''
|
|
22256
|
-
}
|
|
22257
|
-
}
|
|
22258
|
-
},
|
|
22259
|
-
|
|
22260
|
-
urlEncode: (str) => {
|
|
22261
|
-
if (typeof str !== 'string') return ''
|
|
22262
|
-
return encodeURIComponent(str)
|
|
22263
|
-
},
|
|
22264
|
-
|
|
22265
|
-
urlDecode: (str) => {
|
|
22266
|
-
if (typeof str !== 'string') return ''
|
|
22267
|
-
try {
|
|
22268
|
-
return decodeURIComponent(str)
|
|
22269
|
-
} catch {
|
|
22270
|
-
return str
|
|
22271
|
-
}
|
|
22272
|
-
},
|
|
22273
|
-
|
|
22274
|
-
coalesce: (...values) => {
|
|
22275
|
-
for (const value of values) {
|
|
22276
|
-
if (value !== null && value !== undefined) return value
|
|
22277
|
-
}
|
|
22278
|
-
return null
|
|
22279
|
-
},
|
|
22280
|
-
}
|
|
22281
|
-
})()
|
|
22363
|
+
// Transform helper functions generated from the canonical portable factory.
|
|
22364
|
+
const helpers = (${createPortableHelpers.toString()})(${parseHTMLWithRegex.toString()})
|
|
22282
22365
|
`.trim();
|
|
22283
22366
|
var AGENT_EXECUTION_STATUSES = [
|
|
22284
22367
|
"queued",
|
|
@@ -22641,6 +22724,24 @@ var EMIT_ARTIFACT_MARKDOWN_TOOL_ID = "emit_artifact_markdown";
|
|
|
22641
22724
|
var EMIT_ARTIFACT_COMPONENT_TOOL_ID = "emit_artifact_component";
|
|
22642
22725
|
var EMIT_ARTIFACT_MARKDOWN_FULL_ID = `builtin:${EMIT_ARTIFACT_MARKDOWN_TOOL_ID}`;
|
|
22643
22726
|
var EMIT_ARTIFACT_COMPONENT_FULL_ID = `builtin:${EMIT_ARTIFACT_COMPONENT_TOOL_ID}`;
|
|
22727
|
+
var IMPLICIT_ARTIFACT_TOOLS = [
|
|
22728
|
+
{
|
|
22729
|
+
artifactType: "component",
|
|
22730
|
+
toolIds: [
|
|
22731
|
+
"browser:request_handoff",
|
|
22732
|
+
"builtin:browser:request_handoff",
|
|
22733
|
+
"browser-request-handoff",
|
|
22734
|
+
"builtin:browser-request-handoff",
|
|
22735
|
+
"browser:handoff",
|
|
22736
|
+
"builtin:browser:handoff"
|
|
22737
|
+
]
|
|
22738
|
+
}
|
|
22739
|
+
];
|
|
22740
|
+
var IMPLICIT_ARTIFACT_TYPE_BY_TOOL_ID = new Map(
|
|
22741
|
+
IMPLICIT_ARTIFACT_TOOLS.flatMap(
|
|
22742
|
+
({ artifactType, toolIds }) => toolIds.map((id) => [id, artifactType])
|
|
22743
|
+
)
|
|
22744
|
+
);
|
|
22644
22745
|
var ORTHOGONAL_PLATFORM_TOOLS = [
|
|
22645
22746
|
{
|
|
22646
22747
|
"id": "orthogonal:agentmail:create_draft",
|
|
@@ -34953,6 +35054,106 @@ var CORE_BUILTIN_TOOLS_REGISTRY = [
|
|
|
34953
35054
|
executionHint: "platform",
|
|
34954
35055
|
platformKeySupport: true
|
|
34955
35056
|
},
|
|
35057
|
+
// -----------------------------------------------------------------------
|
|
35058
|
+
// Live View + human-in-the-loop handoff.
|
|
35059
|
+
// A Live View URL lets a person watch and drive the same browser session the
|
|
35060
|
+
// agent is using; a handoff formally parks the agent until they are done.
|
|
35061
|
+
// -----------------------------------------------------------------------
|
|
35062
|
+
{
|
|
35063
|
+
id: "browser:get_live_view",
|
|
35064
|
+
aliases: ["browser-get-live-view", "browser:live-view"],
|
|
35065
|
+
name: "Browser Live View",
|
|
35066
|
+
description: 'Get a shareable Live View URL for a browser session: a link a person can open to watch the live page and interact with it directly. Use it to show someone what the browser is doing. Anyone holding the link can control the session, so share it only with the person you are helping. Defaults to "tab" mode, the only mode that supports handoff. If you need a human to actually DO something (sign in, solve a CAPTCHA, complete MFA), use browser:request_handoff instead \u2014 it returns the same kind of URL and also tracks when they finish.',
|
|
35067
|
+
category: BuiltInToolCategory.BROWSER,
|
|
35068
|
+
toolGroup: BuiltInToolGroup.BROWSER_USE,
|
|
35069
|
+
providers: [BuiltInToolProvider.MULTI],
|
|
35070
|
+
parametersSchema: {
|
|
35071
|
+
type: "object",
|
|
35072
|
+
properties: {
|
|
35073
|
+
sessionId: {
|
|
35074
|
+
type: "string",
|
|
35075
|
+
description: "The session ID returned by browser:open"
|
|
35076
|
+
},
|
|
35077
|
+
mode: {
|
|
35078
|
+
type: "string",
|
|
35079
|
+
description: 'Live View layout. "tab" shows just the page and is the only mode that supports handoff. "full" and "devtools" expose the DevTools frontend.',
|
|
35080
|
+
enum: ["tab", "full", "devtools"],
|
|
35081
|
+
default: "tab"
|
|
35082
|
+
},
|
|
35083
|
+
expiresInMs: {
|
|
35084
|
+
type: "number",
|
|
35085
|
+
description: "How long the URL stays valid, in milliseconds (min 60000, max 3600000). Defaults to 600000 (10 minutes).",
|
|
35086
|
+
minimum: 6e4,
|
|
35087
|
+
maximum: 36e5,
|
|
35088
|
+
default: 6e5
|
|
35089
|
+
}
|
|
35090
|
+
},
|
|
35091
|
+
required: ["sessionId"]
|
|
35092
|
+
},
|
|
35093
|
+
documentationUrl: BROWSER_SESSION_DOCUMENTATION_URL,
|
|
35094
|
+
requiresApiKey: false,
|
|
35095
|
+
executionHint: "platform",
|
|
35096
|
+
platformKeySupport: true
|
|
35097
|
+
},
|
|
35098
|
+
{
|
|
35099
|
+
id: "browser:request_handoff",
|
|
35100
|
+
aliases: ["browser-request-handoff", "browser:handoff"],
|
|
35101
|
+
name: "Browser Handoff to Human",
|
|
35102
|
+
description: 'Hand control of a browser session to a human, then continue once they are done. Use this the moment the page needs something you cannot or should not do yourself: a login form, an MFA or 2FA prompt, a CAPTCHA, a payment step, or any decision that needs a person. Never guess at credentials \u2014 hand off instead.\n\nWrite "instructions" as a specific, actionable request that names exactly what to do and what should be true when they finish, for example: "Sign in with the team Google account, approve the 2FA prompt on your phone, and stop once the billing dashboard is visible." The human reads this text next to the live page.\n\nThis tool returns IMMEDIATELY and does not wait for the human \u2014 a handoff can take many minutes while a single tool call is capped at 30 seconds. It normally returns "status": "active" plus a Live View URL the person opens to drive the page; they click "Done" or "Failed" in the Live View UI when finished. If the person happens to finish before this call returns it comes back with "status": "completed" and no URL: the work is already done, so do not poll and do not share a link, just carry on. After calling it: share the Live View URL with the user, then poll browser:get_handoff_state until its "status" is "completed" before doing anything else with the session. Only one handoff can be active per session, and a session allows a limited number of handoffs in total, so do not re-request one while polling.\n\nIf a client tool named "wait_for_human_handoff" is present in your tool list, call it immediately after this tool with { liveViewUrl, handoffId, instructions } from this result; it renders the live page inline for the person. Skip it if no such tool is listed.',
|
|
35103
|
+
category: BuiltInToolCategory.BROWSER,
|
|
35104
|
+
toolGroup: BuiltInToolGroup.BROWSER_USE,
|
|
35105
|
+
providers: [BuiltInToolProvider.MULTI],
|
|
35106
|
+
parametersSchema: {
|
|
35107
|
+
type: "object",
|
|
35108
|
+
properties: {
|
|
35109
|
+
sessionId: {
|
|
35110
|
+
type: "string",
|
|
35111
|
+
description: "The session ID returned by browser:open"
|
|
35112
|
+
},
|
|
35113
|
+
instructions: {
|
|
35114
|
+
type: "string",
|
|
35115
|
+
description: "What the human should do, in specific and actionable terms. Shown to them alongside the live page.",
|
|
35116
|
+
minLength: 1,
|
|
35117
|
+
maxLength: 4e3
|
|
35118
|
+
},
|
|
35119
|
+
timeoutMs: {
|
|
35120
|
+
type: "number",
|
|
35121
|
+
description: "How long to wait for the human, in milliseconds (min 30000, max 1800000). Defaults to 600000 (10 minutes). When it elapses the handoff completes with success=false.",
|
|
35122
|
+
minimum: 3e4,
|
|
35123
|
+
maximum: 18e5,
|
|
35124
|
+
default: 6e5
|
|
35125
|
+
}
|
|
35126
|
+
},
|
|
35127
|
+
required: ["sessionId", "instructions"]
|
|
35128
|
+
},
|
|
35129
|
+
documentationUrl: BROWSER_SESSION_DOCUMENTATION_URL,
|
|
35130
|
+
requiresApiKey: false,
|
|
35131
|
+
executionHint: "platform",
|
|
35132
|
+
platformKeySupport: true
|
|
35133
|
+
},
|
|
35134
|
+
{
|
|
35135
|
+
id: "browser:get_handoff_state",
|
|
35136
|
+
aliases: ["browser-get-handoff-state", "browser:handoff-state"],
|
|
35137
|
+
name: "Browser Handoff State",
|
|
35138
|
+
description: 'Check whether a browser session is still waiting on a human or the handoff has finished. Returns "status": "active" while the person is working, "completed" once they clicked Done or Failed (or the handoff timed out), and "none" if no handoff was ever requested. A completed result also carries the "success" verdict and any reason they gave. Poll this after browser:request_handoff rather than blocking; it returns fast. Keep polling while status is "active"; once it is "completed", resume the task. If "success" is false or absent, take a screenshot with browser:session-screenshot to confirm the real page state before you continue. A result with "reconciled": false means the browser could not be consulted and the status is a best guess, so poll again before acting on it.',
|
|
35139
|
+
category: BuiltInToolCategory.BROWSER,
|
|
35140
|
+
toolGroup: BuiltInToolGroup.BROWSER_USE,
|
|
35141
|
+
providers: [BuiltInToolProvider.MULTI],
|
|
35142
|
+
parametersSchema: {
|
|
35143
|
+
type: "object",
|
|
35144
|
+
properties: {
|
|
35145
|
+
sessionId: {
|
|
35146
|
+
type: "string",
|
|
35147
|
+
description: "The session ID returned by browser:open"
|
|
35148
|
+
}
|
|
35149
|
+
},
|
|
35150
|
+
required: ["sessionId"]
|
|
35151
|
+
},
|
|
35152
|
+
documentationUrl: BROWSER_SESSION_DOCUMENTATION_URL,
|
|
35153
|
+
requiresApiKey: false,
|
|
35154
|
+
executionHint: "platform",
|
|
35155
|
+
platformKeySupport: true
|
|
35156
|
+
},
|
|
34956
35157
|
{
|
|
34957
35158
|
id: "browser:agent",
|
|
34958
35159
|
aliases: ["browser-agent", "browser:task", "browser-task"],
|
|
@@ -41541,10 +41742,7 @@ function extractSecretReferencesFromAnyValue(value, maxDepth = 10) {
|
|
|
41541
41742
|
}
|
|
41542
41743
|
var ACCOUNT_QUERY_PARAM = "account";
|
|
41543
41744
|
function buildDashboardUrl(opts) {
|
|
41544
|
-
|
|
41545
|
-
while (base.endsWith("/")) {
|
|
41546
|
-
base = base.slice(0, -1);
|
|
41547
|
-
}
|
|
41745
|
+
const base = stripTrailingChars(opts.baseUrl, "/");
|
|
41548
41746
|
const path20 = opts.path.startsWith("/") ? opts.path : `/${opts.path}`;
|
|
41549
41747
|
const params = new URLSearchParams();
|
|
41550
41748
|
if (isValidAccountId(opts.account)) {
|
|
@@ -44372,6 +44570,7 @@ ${orthogonalLines.join("\n")}
|
|
|
44372
44570
|
- **Rendered web pages, screenshots, structured extraction, or link harvesting** \u2192 \`builtin:browser:markdown\`, \`builtin:browser:snapshot\`, \`builtin:browser:json\`, \`builtin:browser:scrape\`, \`builtin:browser:links\` (stateless, one-shot \u2014 cheap, no session overhead)
|
|
44373
44571
|
- **Autonomous browsing (just want the outcome \u2014 extract data, complete a form, find info on a page)** \u2192 \`builtin:browser:agent\` with a natural-language \`goal\` plus \`startUrl\` (fresh browser) or \`sessionId\` (continue an existing session). Stagehand handles observe/act/extract; you get back text or, with an \`outputSchema\`, structured JSON. Try this BEFORE reaching for the manual session tools below.
|
|
44374
44572
|
- **Interactive browsing (precise control over individual actions)** \u2192 \`builtin:browser:open\` to start a session, then \`builtin:browser:click\`, \`builtin:browser:type\`, \`builtin:browser:scroll\`, \`builtin:browser:session-screenshot\`, \`builtin:browser:evaluate\`, \`builtin:browser:navigate\`, and \`builtin:browser:close\` when done. Session tools maintain cookies and page state across calls but cost more than stateless tools \u2014 prefer stateless tools when a single page read suffices.
|
|
44573
|
+
- **A browser session hits a login, MFA, CAPTCHA, or payment wall** \u2192 \`builtin:browser:request_handoff\` hands the live page to a human via a shareable Live View URL and returns immediately; poll \`builtin:browser:get_handoff_state\` until they finish. \`builtin:browser:get_live_view\` alone just mints a watch/drive URL without parking the agent. Add these to any agent that browses sites requiring authentication.
|
|
44375
44574
|
- **Multi-page rendered crawling** \u2192 use the flow \`crawl\` step instead of repeated \`browser:*\` tool calls
|
|
44376
44575
|
- **Find people / company enrichment** \u2192 \`platform:orthogonal:apollo:people_match\`, \`platform:orthogonal:apollo:organization_enrich\`
|
|
44377
44576
|
- **Send Slack message** \u2192 \`builtin:slack:send_message\`
|
|
@@ -44438,6 +44637,7 @@ Tools are added to prompt steps via \`tools: { toolIds: ["<id>"] }\`. The AI mod
|
|
|
44438
44637
|
- **Rendered web pages, screenshots, structured extraction, or link harvesting** \u2192 \`builtin:browser:markdown\`, \`builtin:browser:snapshot\`, \`builtin:browser:json\`, \`builtin:browser:scrape\`, \`builtin:browser:links\` (stateless, one-shot)
|
|
44439
44638
|
- **Autonomous browsing (just want the outcome)** \u2192 \`builtin:browser:agent\` with a natural-language \`goal\` plus \`startUrl\` or \`sessionId\` \u2014 try this BEFORE the manual session tools
|
|
44440
44639
|
- **Interactive browsing (precise control)** \u2192 \`builtin:browser:open\`, then \`builtin:browser:click\` / \`type\` / \`scroll\` / \`session-screenshot\` / \`evaluate\` / \`navigate\`, and \`builtin:browser:close\` when done
|
|
44640
|
+
- **Login / MFA / CAPTCHA wall in a browser session** \u2192 \`builtin:browser:request_handoff\` (hands the live page to a human, returns immediately) + \`builtin:browser:get_handoff_state\` to poll for completion
|
|
44441
44641
|
- **Multi-page rendered crawling** \u2192 use the flow \`crawl\` step instead of repeated \`browser:*\` calls
|
|
44442
44642
|
- **Find people / company enrichment** \u2192 \`platform:orthogonal:apollo:people_match\`, \`platform:orthogonal:apollo:organization_enrich\`
|
|
44443
44643
|
- **Send Slack message** \u2192 \`builtin:slack:send_message\`
|
|
@@ -68362,8 +68562,8 @@ import { execFileSync } from "child_process";
|
|
|
68362
68562
|
// src/lib/persona-init.ts
|
|
68363
68563
|
init_credential_store();
|
|
68364
68564
|
|
|
68365
|
-
// ../../node_modules/.pnpm/@runtypelabs+persona@4.
|
|
68366
|
-
var w = "4.
|
|
68565
|
+
// ../../node_modules/.pnpm/@runtypelabs+persona@4.13.0/node_modules/@runtypelabs/persona/dist/codegen.js
|
|
68566
|
+
var w = "4.13.0";
|
|
68367
68567
|
var S = w;
|
|
68368
68568
|
function b(e) {
|
|
68369
68569
|
return e?.cdnBase?.trim().replace(/\/+$/, "").replace(/[\\'"<>`]/g, (r) => `%${r.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`) || `https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${S}/dist`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/cli",
|
|
3
|
-
"version": "2.32.
|
|
3
|
+
"version": "2.32.2",
|
|
4
4
|
"description": "Command-line interface for Runtype AI platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"rosie-skills": "0.8.1",
|
|
28
28
|
"yaml": "^2.9.0",
|
|
29
29
|
"@runtypelabs/ink-components": "0.3.5",
|
|
30
|
-
"@runtypelabs/sdk": "6.3.
|
|
30
|
+
"@runtypelabs/sdk": "6.3.6",
|
|
31
31
|
"@runtypelabs/terminal-animations": "0.2.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@modelcontextprotocol/server": "2.0.0",
|
|
35
|
-
"@runtypelabs/persona": "4.
|
|
35
|
+
"@runtypelabs/persona": "4.13.0",
|
|
36
36
|
"@types/express": "^5.0.6",
|
|
37
37
|
"@types/micromatch": "^4.0.9",
|
|
38
38
|
"@types/node": "^25.3.3",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"tsx": "^4.7.1",
|
|
44
44
|
"typescript": "^6.0.3",
|
|
45
45
|
"vitest": "^4.1.0",
|
|
46
|
-
"@runtypelabs/shared": "3.9.
|
|
46
|
+
"@runtypelabs/shared": "3.9.2"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=22.12.0"
|