@rtrvr-ai/rover 3.0.0 → 3.0.1
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 +181 -0
- package/dist/embed.js +15 -13
- package/dist/index.d.ts +5 -1
- package/dist/previewBootstrap.d.ts +27 -0
- package/dist/previewBootstrap.js +203 -0
- package/dist/rolls-cli.mjs +312 -0
- package/dist/rover.js +15 -13
- package/dist/worker/rover-worker.js +1 -1
- package/package.json +11 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RoverPageCaptureConfig } from '@rover/shared/lib/types/index.js';
|
|
2
2
|
import { type RoverAskUserQuestion, type RoverShortcut, type RoverVoiceConfig } from '@rover/ui';
|
|
3
|
+
import { createRoverBookmarklet, createRoverConsoleSnippet, createRoverScriptTagSnippet, readRoverScriptDataAttributes, type RoverPreviewAttachLaunch } from './previewBootstrap.js';
|
|
3
4
|
import { type RoverCloudCheckpointState } from './cloudCheckpoint.js';
|
|
4
5
|
import type { PersistedRuntimeState, PersistedWorkerState } from './runtimeTypes.js';
|
|
6
|
+
import { type RoverLaunchAttachResponse } from './serverRuntime.js';
|
|
5
7
|
import { type FollowupChatEntry } from './followupChatHeuristics.js';
|
|
6
8
|
export type RoverWebToolsConfig = {
|
|
7
9
|
enableExternalWebContext?: boolean;
|
|
@@ -239,6 +241,7 @@ export type RoverInstance = {
|
|
|
239
241
|
}) => void;
|
|
240
242
|
getState: () => any;
|
|
241
243
|
requestSigned: (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
244
|
+
attachLaunch: (params: RoverPreviewAttachLaunch) => Promise<RoverLaunchAttachResponse | null>;
|
|
242
245
|
registerPromptContextProvider: (provider: RoverPromptContextProvider) => () => void;
|
|
243
246
|
registerTool: (nameOrDef: string | ClientToolDefinition, handler: (args: any) => any | Promise<any>) => void;
|
|
244
247
|
identify: (visitor: {
|
|
@@ -274,6 +277,7 @@ export declare function close(): void;
|
|
|
274
277
|
export declare function show(): void;
|
|
275
278
|
export declare function hide(): void;
|
|
276
279
|
export declare function send(text: string): void;
|
|
280
|
+
export declare function attachLaunch(params: RoverPreviewAttachLaunch): Promise<RoverLaunchAttachResponse | null>;
|
|
277
281
|
export declare function requestSigned(input: string | URL, init?: RequestInit): Promise<Response>;
|
|
278
282
|
export declare function registerPromptContextProvider(provider: RoverPromptContextProvider): () => void;
|
|
279
283
|
export declare function newTask(options?: {
|
|
@@ -297,5 +301,5 @@ export declare const __roverInternalsForTests: {
|
|
|
297
301
|
buildPublicRunStartedPayload: typeof buildPublicRunStartedPayload;
|
|
298
302
|
buildPublicRunLifecyclePayload: typeof buildPublicRunLifecyclePayload;
|
|
299
303
|
};
|
|
304
|
+
export { createRoverBookmarklet, createRoverConsoleSnippet, createRoverScriptTagSnippet, readRoverScriptDataAttributes, };
|
|
300
305
|
export declare function installGlobal(): void;
|
|
301
|
-
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type RoverPreviewAttachLaunch = {
|
|
2
|
+
requestId: string;
|
|
3
|
+
attachToken: string;
|
|
4
|
+
};
|
|
5
|
+
export type RoverPreviewBootstrapConfig = {
|
|
6
|
+
scriptUrl?: string;
|
|
7
|
+
siteId: string;
|
|
8
|
+
publicKey?: string;
|
|
9
|
+
sessionToken?: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
siteKeyId?: string;
|
|
12
|
+
apiBase?: string;
|
|
13
|
+
workerUrl?: string;
|
|
14
|
+
allowedDomains?: string[];
|
|
15
|
+
domainScopeMode?: 'host_only' | 'registrable_domain';
|
|
16
|
+
externalNavigationPolicy?: 'open_new_tab_notice' | 'block' | 'allow';
|
|
17
|
+
sessionScope?: 'shared_site' | 'tab';
|
|
18
|
+
openOnInit?: boolean;
|
|
19
|
+
mode?: 'safe' | 'full';
|
|
20
|
+
allowActions?: boolean;
|
|
21
|
+
attachLaunch?: RoverPreviewAttachLaunch;
|
|
22
|
+
};
|
|
23
|
+
export type RoverScriptAttributeSource = Pick<HTMLScriptElement, 'getAttribute'>;
|
|
24
|
+
export declare function createRoverConsoleSnippet(config: RoverPreviewBootstrapConfig): string;
|
|
25
|
+
export declare function createRoverBookmarklet(config: RoverPreviewBootstrapConfig): string;
|
|
26
|
+
export declare function createRoverScriptTagSnippet(config: RoverPreviewBootstrapConfig): string;
|
|
27
|
+
export declare function readRoverScriptDataAttributes(scriptEl: RoverScriptAttributeSource): RoverPreviewBootstrapConfig | null;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const DEFAULT_EMBED_SCRIPT_URL = 'https://rover.rtrvr.ai/embed.js';
|
|
2
|
+
function toStringValue(value) {
|
|
3
|
+
return String(value || '').trim();
|
|
4
|
+
}
|
|
5
|
+
function escapeHtmlAttr(value) {
|
|
6
|
+
return String(value || '')
|
|
7
|
+
.replace(/&/g, '&')
|
|
8
|
+
.replace(/"/g, '"')
|
|
9
|
+
.replace(/</g, '<')
|
|
10
|
+
.replace(/>/g, '>');
|
|
11
|
+
}
|
|
12
|
+
function parseBooleanAttr(value) {
|
|
13
|
+
const normalized = toStringValue(value).toLowerCase();
|
|
14
|
+
if (!normalized)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (['1', 'true', 'yes', 'on'].includes(normalized))
|
|
17
|
+
return true;
|
|
18
|
+
if (['0', 'false', 'no', 'off'].includes(normalized))
|
|
19
|
+
return false;
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
function parseCsvList(value) {
|
|
23
|
+
const items = toStringValue(value)
|
|
24
|
+
.split(',')
|
|
25
|
+
.map(item => item.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
if (!items.length)
|
|
28
|
+
return undefined;
|
|
29
|
+
return Array.from(new Set(items));
|
|
30
|
+
}
|
|
31
|
+
function normalizeBootstrapConfig(config) {
|
|
32
|
+
return {
|
|
33
|
+
...config,
|
|
34
|
+
scriptUrl: toStringValue(config.scriptUrl) || DEFAULT_EMBED_SCRIPT_URL,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function buildBootstrapPayload(config) {
|
|
38
|
+
const normalized = normalizeBootstrapConfig(config);
|
|
39
|
+
const payload = {
|
|
40
|
+
siteId: normalized.siteId,
|
|
41
|
+
};
|
|
42
|
+
if (normalized.publicKey)
|
|
43
|
+
payload.publicKey = normalized.publicKey;
|
|
44
|
+
if (normalized.sessionToken)
|
|
45
|
+
payload.sessionToken = normalized.sessionToken;
|
|
46
|
+
if (normalized.sessionId)
|
|
47
|
+
payload.sessionId = normalized.sessionId;
|
|
48
|
+
if (normalized.siteKeyId)
|
|
49
|
+
payload.siteKeyId = normalized.siteKeyId;
|
|
50
|
+
if (normalized.apiBase)
|
|
51
|
+
payload.apiBase = normalized.apiBase;
|
|
52
|
+
if (normalized.workerUrl)
|
|
53
|
+
payload.workerUrl = normalized.workerUrl;
|
|
54
|
+
if (normalized.allowedDomains?.length)
|
|
55
|
+
payload.allowedDomains = normalized.allowedDomains;
|
|
56
|
+
if (normalized.domainScopeMode)
|
|
57
|
+
payload.domainScopeMode = normalized.domainScopeMode;
|
|
58
|
+
if (normalized.externalNavigationPolicy)
|
|
59
|
+
payload.externalNavigationPolicy = normalized.externalNavigationPolicy;
|
|
60
|
+
if (normalized.sessionScope)
|
|
61
|
+
payload.sessionScope = normalized.sessionScope;
|
|
62
|
+
if (typeof normalized.openOnInit === 'boolean')
|
|
63
|
+
payload.openOnInit = normalized.openOnInit;
|
|
64
|
+
if (normalized.mode)
|
|
65
|
+
payload.mode = normalized.mode;
|
|
66
|
+
if (typeof normalized.allowActions === 'boolean')
|
|
67
|
+
payload.allowActions = normalized.allowActions;
|
|
68
|
+
return payload;
|
|
69
|
+
}
|
|
70
|
+
function buildQueueStub() {
|
|
71
|
+
return [
|
|
72
|
+
'(function(){',
|
|
73
|
+
' var r = window.rover = window.rover || function(){',
|
|
74
|
+
' (r.q = r.q || []).push(arguments);',
|
|
75
|
+
' };',
|
|
76
|
+
' r.l = +new Date();',
|
|
77
|
+
'})();',
|
|
78
|
+
].join('\n');
|
|
79
|
+
}
|
|
80
|
+
function buildCompactQueueStub() {
|
|
81
|
+
return '(function(){var r=window.rover=window.rover||function(){(r.q=r.q||[]).push(arguments)};r.l=+new Date()})();';
|
|
82
|
+
}
|
|
83
|
+
function buildConsoleScript(config, compact = false) {
|
|
84
|
+
const normalized = normalizeBootstrapConfig(config);
|
|
85
|
+
const payloadJson = compact
|
|
86
|
+
? JSON.stringify(buildBootstrapPayload(normalized))
|
|
87
|
+
: JSON.stringify(buildBootstrapPayload(normalized), null, 2);
|
|
88
|
+
const attachJson = normalized.attachLaunch
|
|
89
|
+
? (compact ? JSON.stringify(normalized.attachLaunch) : JSON.stringify(normalized.attachLaunch, null, 2))
|
|
90
|
+
: '';
|
|
91
|
+
const scriptUrl = JSON.stringify(normalized.scriptUrl);
|
|
92
|
+
if (compact) {
|
|
93
|
+
const parts = [
|
|
94
|
+
buildCompactQueueStub(),
|
|
95
|
+
`rover('boot', ${payloadJson});`,
|
|
96
|
+
normalized.attachLaunch ? `rover('attachLaunch', ${attachJson});` : '',
|
|
97
|
+
`(function(){var s=document.createElement('script');s.src=${scriptUrl};s.async=true;(document.head||document.documentElement).appendChild(s)})();`,
|
|
98
|
+
];
|
|
99
|
+
return parts.filter(Boolean).join('');
|
|
100
|
+
}
|
|
101
|
+
const lines = [
|
|
102
|
+
buildQueueStub(),
|
|
103
|
+
'',
|
|
104
|
+
`rover('boot', ${payloadJson});`,
|
|
105
|
+
];
|
|
106
|
+
if (normalized.attachLaunch) {
|
|
107
|
+
lines.push(`rover('attachLaunch', ${attachJson});`);
|
|
108
|
+
}
|
|
109
|
+
lines.push('', '(function(){', ' var s = document.createElement("script");', ` s.src = ${scriptUrl};`, ' s.async = true;', ' (document.head || document.documentElement).appendChild(s);', '})();');
|
|
110
|
+
return lines.join('\n');
|
|
111
|
+
}
|
|
112
|
+
export function createRoverConsoleSnippet(config) {
|
|
113
|
+
return buildConsoleScript(config, false);
|
|
114
|
+
}
|
|
115
|
+
export function createRoverBookmarklet(config) {
|
|
116
|
+
return `javascript:${buildConsoleScript(config, true)}`;
|
|
117
|
+
}
|
|
118
|
+
export function createRoverScriptTagSnippet(config) {
|
|
119
|
+
const normalized = normalizeBootstrapConfig(config);
|
|
120
|
+
const attrs = [
|
|
121
|
+
`src="${escapeHtmlAttr(normalized.scriptUrl)}"`,
|
|
122
|
+
`data-site-id="${escapeHtmlAttr(normalized.siteId)}"`,
|
|
123
|
+
];
|
|
124
|
+
if (normalized.publicKey)
|
|
125
|
+
attrs.push(`data-public-key="${escapeHtmlAttr(normalized.publicKey)}"`);
|
|
126
|
+
if (normalized.sessionToken)
|
|
127
|
+
attrs.push(`data-session-token="${escapeHtmlAttr(normalized.sessionToken)}"`);
|
|
128
|
+
if (normalized.sessionId)
|
|
129
|
+
attrs.push(`data-session-id="${escapeHtmlAttr(normalized.sessionId)}"`);
|
|
130
|
+
if (normalized.siteKeyId)
|
|
131
|
+
attrs.push(`data-site-key-id="${escapeHtmlAttr(normalized.siteKeyId)}"`);
|
|
132
|
+
if (normalized.apiBase)
|
|
133
|
+
attrs.push(`data-api-base="${escapeHtmlAttr(normalized.apiBase)}"`);
|
|
134
|
+
if (normalized.workerUrl)
|
|
135
|
+
attrs.push(`data-worker-url="${escapeHtmlAttr(normalized.workerUrl)}"`);
|
|
136
|
+
if (normalized.allowedDomains?.length)
|
|
137
|
+
attrs.push(`data-allowed-domains="${escapeHtmlAttr(normalized.allowedDomains.join(','))}"`);
|
|
138
|
+
if (normalized.domainScopeMode)
|
|
139
|
+
attrs.push(`data-domain-scope-mode="${escapeHtmlAttr(normalized.domainScopeMode)}"`);
|
|
140
|
+
if (normalized.externalNavigationPolicy)
|
|
141
|
+
attrs.push(`data-external-navigation-policy="${escapeHtmlAttr(normalized.externalNavigationPolicy)}"`);
|
|
142
|
+
if (normalized.sessionScope)
|
|
143
|
+
attrs.push(`data-session-scope="${escapeHtmlAttr(normalized.sessionScope)}"`);
|
|
144
|
+
if (typeof normalized.openOnInit === 'boolean')
|
|
145
|
+
attrs.push(`data-open-on-init="${escapeHtmlAttr(String(normalized.openOnInit))}"`);
|
|
146
|
+
if (normalized.mode)
|
|
147
|
+
attrs.push(`data-mode="${escapeHtmlAttr(normalized.mode)}"`);
|
|
148
|
+
if (typeof normalized.allowActions === 'boolean')
|
|
149
|
+
attrs.push(`data-allow-actions="${escapeHtmlAttr(String(normalized.allowActions))}"`);
|
|
150
|
+
return `<script ${attrs.join(' ')}></script>`;
|
|
151
|
+
}
|
|
152
|
+
export function readRoverScriptDataAttributes(scriptEl) {
|
|
153
|
+
const siteId = toStringValue(scriptEl.getAttribute('data-site-id'));
|
|
154
|
+
const publicKey = toStringValue(scriptEl.getAttribute('data-public-key'));
|
|
155
|
+
const sessionToken = toStringValue(scriptEl.getAttribute('data-session-token'));
|
|
156
|
+
if (!siteId || (!publicKey && !sessionToken))
|
|
157
|
+
return null;
|
|
158
|
+
const config = {
|
|
159
|
+
siteId,
|
|
160
|
+
};
|
|
161
|
+
if (publicKey)
|
|
162
|
+
config.publicKey = publicKey;
|
|
163
|
+
if (sessionToken)
|
|
164
|
+
config.sessionToken = sessionToken;
|
|
165
|
+
const sessionId = toStringValue(scriptEl.getAttribute('data-session-id'));
|
|
166
|
+
if (sessionId)
|
|
167
|
+
config.sessionId = sessionId;
|
|
168
|
+
const siteKeyId = toStringValue(scriptEl.getAttribute('data-site-key-id'));
|
|
169
|
+
if (siteKeyId)
|
|
170
|
+
config.siteKeyId = siteKeyId;
|
|
171
|
+
const apiBase = toStringValue(scriptEl.getAttribute('data-api-base'));
|
|
172
|
+
if (apiBase)
|
|
173
|
+
config.apiBase = apiBase;
|
|
174
|
+
const workerUrl = toStringValue(scriptEl.getAttribute('data-worker-url'));
|
|
175
|
+
if (workerUrl)
|
|
176
|
+
config.workerUrl = workerUrl;
|
|
177
|
+
const allowedDomains = parseCsvList(scriptEl.getAttribute('data-allowed-domains'));
|
|
178
|
+
if (allowedDomains)
|
|
179
|
+
config.allowedDomains = allowedDomains;
|
|
180
|
+
const domainScopeMode = toStringValue(scriptEl.getAttribute('data-domain-scope-mode'));
|
|
181
|
+
if (domainScopeMode === 'host_only' || domainScopeMode === 'registrable_domain') {
|
|
182
|
+
config.domainScopeMode = domainScopeMode;
|
|
183
|
+
}
|
|
184
|
+
const externalNavigationPolicy = toStringValue(scriptEl.getAttribute('data-external-navigation-policy'));
|
|
185
|
+
if (externalNavigationPolicy === 'open_new_tab_notice' || externalNavigationPolicy === 'block' || externalNavigationPolicy === 'allow') {
|
|
186
|
+
config.externalNavigationPolicy = externalNavigationPolicy;
|
|
187
|
+
}
|
|
188
|
+
const sessionScope = toStringValue(scriptEl.getAttribute('data-session-scope'));
|
|
189
|
+
if (sessionScope === 'shared_site' || sessionScope === 'tab') {
|
|
190
|
+
config.sessionScope = sessionScope;
|
|
191
|
+
}
|
|
192
|
+
const openOnInit = parseBooleanAttr(scriptEl.getAttribute('data-open-on-init'));
|
|
193
|
+
if (typeof openOnInit === 'boolean')
|
|
194
|
+
config.openOnInit = openOnInit;
|
|
195
|
+
const mode = toStringValue(scriptEl.getAttribute('data-mode'));
|
|
196
|
+
if (mode === 'safe' || mode === 'full') {
|
|
197
|
+
config.mode = mode;
|
|
198
|
+
}
|
|
199
|
+
const allowActions = parseBooleanAttr(scriptEl.getAttribute('data-allow-actions'));
|
|
200
|
+
if (typeof allowActions === 'boolean')
|
|
201
|
+
config.allowActions = allowActions;
|
|
202
|
+
return config;
|
|
203
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../apps/rolls/src/terminal.mjs
|
|
4
|
+
var ESC = "\x1B[";
|
|
5
|
+
var orange = (s) => `\x1B[38;2;255;76;0m${s}\x1B[0m`;
|
|
6
|
+
var amber = (s) => `\x1B[38;2;255;184;0m${s}\x1B[0m`;
|
|
7
|
+
var green = (s) => `\x1B[38;2;74;222;128m${s}\x1B[0m`;
|
|
8
|
+
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
9
|
+
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
10
|
+
var red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
11
|
+
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
12
|
+
var clearScreen = () => process.stdout.write(`${ESC}2J${ESC}H`);
|
|
13
|
+
var hideCursor = () => process.stdout.write(`${ESC}?25l`);
|
|
14
|
+
var showCursor = () => process.stdout.write(`${ESC}?25h`);
|
|
15
|
+
function typewrite(text, delay = 40) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
let i = 0;
|
|
18
|
+
const tick = () => {
|
|
19
|
+
if (i < text.length) {
|
|
20
|
+
process.stdout.write(text[i]);
|
|
21
|
+
i++;
|
|
22
|
+
setTimeout(tick, delay);
|
|
23
|
+
} else {
|
|
24
|
+
resolve();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
tick();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
31
|
+
function waitForEnter() {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
if (process.stdin.isTTY) {
|
|
34
|
+
process.stdin.setRawMode(true);
|
|
35
|
+
}
|
|
36
|
+
process.stdin.resume();
|
|
37
|
+
process.stdin.once("data", (key) => {
|
|
38
|
+
if (process.stdin.isTTY) {
|
|
39
|
+
process.stdin.setRawMode(false);
|
|
40
|
+
}
|
|
41
|
+
if (key[0] === 3) {
|
|
42
|
+
showCursor();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
resolve();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function selectMenu(items, renderFn) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
let selected = 0;
|
|
52
|
+
const render = () => {
|
|
53
|
+
renderFn(items, selected);
|
|
54
|
+
};
|
|
55
|
+
render();
|
|
56
|
+
if (process.stdin.isTTY) {
|
|
57
|
+
process.stdin.setRawMode(true);
|
|
58
|
+
}
|
|
59
|
+
process.stdin.resume();
|
|
60
|
+
process.stdin.setEncoding("utf8");
|
|
61
|
+
const onData = (key) => {
|
|
62
|
+
if (key === "") {
|
|
63
|
+
process.stdin.removeListener("data", onData);
|
|
64
|
+
if (process.stdin.isTTY) {
|
|
65
|
+
process.stdin.setRawMode(false);
|
|
66
|
+
}
|
|
67
|
+
showCursor();
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
if (key === "\x1B[A") {
|
|
71
|
+
selected = (selected - 1 + items.length) % items.length;
|
|
72
|
+
render();
|
|
73
|
+
}
|
|
74
|
+
if (key === "\x1B[B") {
|
|
75
|
+
selected = (selected + 1) % items.length;
|
|
76
|
+
render();
|
|
77
|
+
}
|
|
78
|
+
if (key === "\r" || key === "\n") {
|
|
79
|
+
process.stdin.removeListener("data", onData);
|
|
80
|
+
if (process.stdin.isTTY) {
|
|
81
|
+
process.stdin.setRawMode(false);
|
|
82
|
+
}
|
|
83
|
+
resolve(items[selected]);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
process.stdin.on("data", onData);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function stripAnsi(s) {
|
|
90
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
91
|
+
}
|
|
92
|
+
function box(lines, { width = 56, padding = 2, double = true } = {}) {
|
|
93
|
+
const [tl, tr, bl, br, h, v] = double ? ["\u2554", "\u2557", "\u255A", "\u255D", "\u2550", "\u2551"] : ["\u250C", "\u2510", "\u2514", "\u2518", "\u2500", "\u2502"];
|
|
94
|
+
const inner = width - 2;
|
|
95
|
+
const out = [];
|
|
96
|
+
out.push(` ${tl}${h.repeat(inner)}${tr}`);
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
const stripped = stripAnsi(line);
|
|
99
|
+
const rightPad = Math.max(0, inner - padding - stripped.length);
|
|
100
|
+
out.push(` ${v}${" ".repeat(padding)}${line}${" ".repeat(rightPad)}${v}`);
|
|
101
|
+
}
|
|
102
|
+
out.push(` ${bl}${h.repeat(inner)}${br}`);
|
|
103
|
+
return out.join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ../../apps/rolls/src/art.mjs
|
|
107
|
+
var LOGO_LINES = [
|
|
108
|
+
` ___ _____ ___ _ _ ___ ___ ___ _ _ `,
|
|
109
|
+
`| _ \\|_ _|| _ \\| | | || _ \\ | _ \\ / _ \\| | | | `,
|
|
110
|
+
`| / | | | /| |_| || / | /| (_) | |__ | |__ `,
|
|
111
|
+
`|_|_\\ |_| |_|_\\ \\___/ |_|_\\ |_|_\\ \\___/|____|____| `
|
|
112
|
+
];
|
|
113
|
+
var LOGO_COLORED = LOGO_LINES.map((l) => orange(l));
|
|
114
|
+
var CHICKEN = [
|
|
115
|
+
` _ _`,
|
|
116
|
+
` (o >`,
|
|
117
|
+
` //\\`,
|
|
118
|
+
` V_/_`
|
|
119
|
+
];
|
|
120
|
+
var CHICKEN_COLORED = CHICKEN.map((l) => amber(l));
|
|
121
|
+
|
|
122
|
+
// ../../apps/rolls/src/menu.mjs
|
|
123
|
+
var MENU_ITEMS = [
|
|
124
|
+
{
|
|
125
|
+
id: "series-a-seekh",
|
|
126
|
+
emoji: "\u{1F959}",
|
|
127
|
+
name: "The Series A Seekh Kebab Roll",
|
|
128
|
+
price: "$4.20M (pre-revenue)",
|
|
129
|
+
subtitle: "Pre-money valuation: delicious",
|
|
130
|
+
spice: 3
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "pivot-paneer",
|
|
134
|
+
emoji: "\u{1FAD3}",
|
|
135
|
+
name: "Pivot Paneer Tikka Roll",
|
|
136
|
+
price: "2 SAFE notes",
|
|
137
|
+
subtitle: "We were a SaaS company once",
|
|
138
|
+
spice: 2
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: "burn-rate-bhurji",
|
|
142
|
+
emoji: "\u{1F373}",
|
|
143
|
+
name: "The Burn Rate Bhurji Roll",
|
|
144
|
+
price: "$0 (bootstrapped)",
|
|
145
|
+
subtitle: "Consuming capital, one bite at a time",
|
|
146
|
+
spice: 4
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "yc-chicken-tikka",
|
|
150
|
+
emoji: "\u{1F357}",
|
|
151
|
+
name: "YC Chicken Tikka Roll",
|
|
152
|
+
price: "$500K (standard deal)",
|
|
153
|
+
subtitle: "Backed by garlic chutney",
|
|
154
|
+
spice: 3
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "cap-table-kathi",
|
|
158
|
+
emoji: "\u{1F32F}",
|
|
159
|
+
name: "Cap Table Kathi Roll",
|
|
160
|
+
price: "409A pending",
|
|
161
|
+
subtitle: "Ownership is complicated",
|
|
162
|
+
spice: 2
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "runway-raita",
|
|
166
|
+
emoji: "\u{1F963}",
|
|
167
|
+
name: "Runway Extension Raita",
|
|
168
|
+
price: "Free (angel round)",
|
|
169
|
+
subtitle: "It's a side. Like your consulting gig.",
|
|
170
|
+
spice: 0
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
// ../../apps/rolls/src/order-flow.mjs
|
|
175
|
+
async function playOrderFlow(item) {
|
|
176
|
+
clearScreen();
|
|
177
|
+
const cmd = ` $ rover order --item "${item.name}" --extra-spicy`;
|
|
178
|
+
await typewrite(green(cmd), 30);
|
|
179
|
+
process.stdout.write("\n\n");
|
|
180
|
+
const lines = [
|
|
181
|
+
{ text: "Booting agent runtime...", delay: 600 },
|
|
182
|
+
{ text: "Authenticating with rtrvr-rolls-HQ...", delay: 800 },
|
|
183
|
+
{ text: "Agent connected. Model: gpt-4-turbo-tandoori", delay: 500 },
|
|
184
|
+
{ text: "Navigating to kitchen API...", delay: 900 },
|
|
185
|
+
{ text: `Locating: "${item.name}"`, delay: 700 },
|
|
186
|
+
{ text: "Adding to cart... done", delay: 500 },
|
|
187
|
+
{ text: "Applying coupon: DEMO-DAY-DISCOUNT", delay: 600 },
|
|
188
|
+
{ text: "Coupon rejected: this is not a real restaurant", delay: 400, color: "red" },
|
|
189
|
+
{ text: "Processing payment via npm credits...", delay: 800 },
|
|
190
|
+
{ text: "Contacting kitchen microservice...", delay: 1e3 },
|
|
191
|
+
{ text: "Kitchen API returned: 418 I'm a teapot", delay: 500, color: "red" },
|
|
192
|
+
{ text: "Retrying with exponential backoff and extra masala...", delay: 1200 },
|
|
193
|
+
{ text: "Hmm...", delay: 800 },
|
|
194
|
+
{ text: "Wait a second...", delay: 1e3 },
|
|
195
|
+
{ text: "...", delay: 1500 }
|
|
196
|
+
];
|
|
197
|
+
for (const line of lines) {
|
|
198
|
+
const prefix = dim(" [rover] ");
|
|
199
|
+
let text = line.text;
|
|
200
|
+
if (line.color === "red") {
|
|
201
|
+
text = red(text);
|
|
202
|
+
} else {
|
|
203
|
+
text = cyan(text);
|
|
204
|
+
}
|
|
205
|
+
process.stdout.write(prefix);
|
|
206
|
+
await typewrite(text, 25);
|
|
207
|
+
process.stdout.write("\n");
|
|
208
|
+
await sleep(line.delay);
|
|
209
|
+
}
|
|
210
|
+
await sleep(500);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ../../apps/rolls/src/reveal.mjs
|
|
214
|
+
async function showReveal() {
|
|
215
|
+
clearScreen();
|
|
216
|
+
const lines = [
|
|
217
|
+
"",
|
|
218
|
+
bold(orange(" \u{1F389} APRIL FOOLS! \u{1F389}")),
|
|
219
|
+
"",
|
|
220
|
+
` rtrvr rolls isn't real (yet).`,
|
|
221
|
+
` But ${bold("Rover")} is.`,
|
|
222
|
+
"",
|
|
223
|
+
" Rover is an AI agent that actually browses the web",
|
|
224
|
+
" for your users. It clicks, types, navigates, and",
|
|
225
|
+
` extracts ${dim("\u2014 so your users don't have to.")}`,
|
|
226
|
+
"",
|
|
227
|
+
` No tandoori required.`,
|
|
228
|
+
"",
|
|
229
|
+
green(" npx -p @rtrvr-ai/rover rtrvr-rolls"),
|
|
230
|
+
amber(" https://rtrvr.ai/rover"),
|
|
231
|
+
"",
|
|
232
|
+
dim(" \u2500\u2500 No chickens were harmed."),
|
|
233
|
+
dim(" Some VCs were mildly offended. \u2500\u2500"),
|
|
234
|
+
""
|
|
235
|
+
];
|
|
236
|
+
console.log(box(lines, { width: 58 }));
|
|
237
|
+
await sleep(500);
|
|
238
|
+
showCursor();
|
|
239
|
+
process.stdout.write("\n");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ../../apps/rolls/src/index.mjs
|
|
243
|
+
function cleanup() {
|
|
244
|
+
showCursor();
|
|
245
|
+
process.stdout.write("\n");
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
process.on("SIGINT", cleanup);
|
|
249
|
+
process.on("SIGTERM", cleanup);
|
|
250
|
+
async function showWelcome() {
|
|
251
|
+
clearScreen();
|
|
252
|
+
hideCursor();
|
|
253
|
+
const lines = [
|
|
254
|
+
"",
|
|
255
|
+
...LOGO_COLORED,
|
|
256
|
+
"",
|
|
257
|
+
bold(" Protein-packed rolls for founders who forgot to eat"),
|
|
258
|
+
orange(" The world's first agentic restaurant."),
|
|
259
|
+
"",
|
|
260
|
+
dim(" Press ENTER to view the menu..."),
|
|
261
|
+
""
|
|
262
|
+
];
|
|
263
|
+
console.log(box(lines, { width: 58 }));
|
|
264
|
+
await waitForEnter();
|
|
265
|
+
}
|
|
266
|
+
async function showMenu() {
|
|
267
|
+
const renderMenu = (items, selectedIndex) => {
|
|
268
|
+
clearScreen();
|
|
269
|
+
hideCursor();
|
|
270
|
+
const W = 47;
|
|
271
|
+
const h = "\u2500";
|
|
272
|
+
const header = [
|
|
273
|
+
` \u250C${h.repeat(W)}\u2510`,
|
|
274
|
+
` \u2502 ${bold("THE MENU")}${" ".repeat(W - 38)}${dim("rtrvr rolls est. 2026")} \u2502`,
|
|
275
|
+
` \u251C${h.repeat(W)}\u2524`
|
|
276
|
+
];
|
|
277
|
+
const body = [];
|
|
278
|
+
body.push(` \u2502${" ".repeat(W)}\u2502`);
|
|
279
|
+
for (let i = 0; i < items.length; i++) {
|
|
280
|
+
const item = items[i];
|
|
281
|
+
const pointer = i === selectedIndex ? orange("\u276F") : " ";
|
|
282
|
+
const nameColor = i === selectedIndex ? orange : (s) => s;
|
|
283
|
+
const nameLine = ` ${pointer} ${item.emoji} ${nameColor(bold(item.name))}`;
|
|
284
|
+
const priceLine = ` ${amber(item.price)}`;
|
|
285
|
+
const subLine = ` ${dim(`"${item.subtitle}"`)}`;
|
|
286
|
+
const padLine = (line) => {
|
|
287
|
+
const stripped = stripAnsi(line);
|
|
288
|
+
const pad = Math.max(0, W - stripped.length);
|
|
289
|
+
return ` \u2502${line}${" ".repeat(pad)}\u2502`;
|
|
290
|
+
};
|
|
291
|
+
body.push(padLine(nameLine));
|
|
292
|
+
body.push(padLine(priceLine));
|
|
293
|
+
body.push(padLine(subLine));
|
|
294
|
+
body.push(` \u2502${" ".repeat(W)}\u2502`);
|
|
295
|
+
}
|
|
296
|
+
const footer = [
|
|
297
|
+
` \u2502 ${dim("\u2191/\u2193 to browse ENTER to order")}${" ".repeat(W - 33)}\u2502`,
|
|
298
|
+
` \u2514${h.repeat(W)}\u2524`
|
|
299
|
+
];
|
|
300
|
+
process.stdout.write([...header, ...body, ...footer].join("\n") + "\n");
|
|
301
|
+
};
|
|
302
|
+
return selectMenu(MENU_ITEMS, renderMenu);
|
|
303
|
+
}
|
|
304
|
+
async function run() {
|
|
305
|
+
await showWelcome();
|
|
306
|
+
const selected = await showMenu();
|
|
307
|
+
await playOrderFlow(selected);
|
|
308
|
+
await showReveal();
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
run
|
|
312
|
+
};
|