@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.
Files changed (2) hide show
  1. package/dist/index.js +481 -281
  2. 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-UIQ7MVBO.mjs
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 (from packages/shared/src/transform-helpers.ts)
22009
- const helpers = (function createHelpers() {
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
- let base = opts.baseUrl;
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.12.0/node_modules/@runtypelabs/persona/dist/codegen.js
68366
- var w = "4.12.0";
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.0",
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.4",
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.12.0",
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.0"
46
+ "@runtypelabs/shared": "3.9.2"
47
47
  },
48
48
  "engines": {
49
49
  "node": ">=22.12.0"