@inneranimalmedia/agentsam-sdk 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +2 -1
- package/src/cli.js +20 -3
- package/src/commands/http-server.js +97 -0
- package/src/commands/tunnel.js +232 -0
- package/src/lib/gorilla-template.js +54 -0
- package/src/lib/local-scaffold.js +32 -8
- package/src/ui/splash-xterm.js +290 -0
- package/src/ui/splash.js +426 -0
- package/src/ui/terminal-lanes.css +162 -0
- package/src/ui/theme.js +315 -0
- package/templates/gorilla-shell/App.tsx +639 -0
- package/templates/gorilla-shell/README.md +22 -0
- package/templates/gorilla-shell/index.html +15 -0
- package/templates/gorilla-shell/main.jsx +9 -0
- package/templates/gorilla-shell/package.json +19 -0
- package/templates/gorilla-shell/vite.config.js +21 -0
- package/test/smoke.mjs +16 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file src/ui/splash-xterm.js
|
|
3
|
+
* @description In-app splash renderer for xterm.js panels.
|
|
4
|
+
*
|
|
5
|
+
* Same choreography as splash.js (CLI) but writes to an xterm Terminal
|
|
6
|
+
* instance instead of process.stdout. Used by XTermShell.tsx.
|
|
7
|
+
*
|
|
8
|
+
* Usage in XTermShell.tsx:
|
|
9
|
+
* import { runXtermSplash } from '@inneranimalmedia/agentsam-sdk/ui/splash-xterm';
|
|
10
|
+
* import { getXtermOptions } from '@inneranimalmedia/agentsam-sdk/ui/theme';
|
|
11
|
+
*
|
|
12
|
+
* const targetType = connection?.target_type ?? 'platform_vm';
|
|
13
|
+
* const term = new Terminal({
|
|
14
|
+
* ...getXtermOptions(targetType),
|
|
15
|
+
* cols: dimensions.cols,
|
|
16
|
+
* rows: dimensions.rows,
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // After term.open(containerRef.current):
|
|
20
|
+
* const stopFlicker = await runXtermSplash(term, {
|
|
21
|
+
* apiBase: 'https://inneranimalmedia.com',
|
|
22
|
+
* workspaceId: activeWorkspace?.id,
|
|
23
|
+
* authToken: session?.token,
|
|
24
|
+
* targetType,
|
|
25
|
+
* skipArt: !isFirstSession,
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // On cleanup:
|
|
29
|
+
* return () => { stopFlicker(); term.dispose(); };
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { PALETTE, getLaneTheme } from './theme.js';
|
|
33
|
+
import { probeAll } from './splash.js';
|
|
34
|
+
|
|
35
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
// xterm write helpers
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const ESC = '\x1b';
|
|
40
|
+
const CSI = `${ESC}[`;
|
|
41
|
+
|
|
42
|
+
const X = {
|
|
43
|
+
reset: `${CSI}0m`,
|
|
44
|
+
bold: `${CSI}1m`,
|
|
45
|
+
hide: `${CSI}?25l`,
|
|
46
|
+
show: `${CSI}?25h`,
|
|
47
|
+
clearLine: `${CSI}2K\r`,
|
|
48
|
+
up: (n = 1) => `${CSI}${n}A`,
|
|
49
|
+
fg: (hex) => {
|
|
50
|
+
const h = hex.replace('#', '');
|
|
51
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
52
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
53
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
54
|
+
return `${CSI}38;2;${r};${g};${b}m`;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
59
|
+
|
|
60
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
61
|
+
// COLOR BUILDER (per-lane)
|
|
62
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
function buildColors() {
|
|
65
|
+
const R = X.reset;
|
|
66
|
+
const B = X.bold;
|
|
67
|
+
return {
|
|
68
|
+
R, B,
|
|
69
|
+
TEAL: X.fg(PALETTE.teal300),
|
|
70
|
+
AMBER: X.fg(PALETTE.amber300),
|
|
71
|
+
STONE: X.fg('#3a3530'),
|
|
72
|
+
STONE2: X.fg('#2a2520'),
|
|
73
|
+
VINE: X.fg('#1a4a2a'),
|
|
74
|
+
VINE2: X.fg('#0f6b3a'),
|
|
75
|
+
FIRE1: X.fg('#f97316'),
|
|
76
|
+
FIRE2: X.fg('#ef4444'),
|
|
77
|
+
FIREY: X.fg('#fef08a'),
|
|
78
|
+
GRILL: X.fg('#1e2830'),
|
|
79
|
+
GRILL2: X.fg('#2d3d4a'),
|
|
80
|
+
PELT: X.fg('#8b6f5a'),
|
|
81
|
+
DIM: X.fg('#444444'),
|
|
82
|
+
GHOST: X.fg('#888888'),
|
|
83
|
+
WHT: X.fg('#e2e8f0'),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
// SCENE DATA
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
function gorillaRows(C) {
|
|
92
|
+
const { R, GRILL, GRILL2, PELT, DIM } = C;
|
|
93
|
+
return [
|
|
94
|
+
` ${DIM}░░▒${R}${GRILL2}▓▓▓▓▓▓▓▓${R}${DIM}▒░░${R}`,
|
|
95
|
+
` ${DIM}░${R}${GRILL2}▒▓▓${R}${GRILL}█████████████${R}${GRILL2}▓▓▒${R}${DIM}░${R}`,
|
|
96
|
+
` ${GRILL2}▒▓${R}${GRILL}███${R}${GRILL2}▒▒${R}${GRILL}████████${R}${GRILL2}▒▒${R}${GRILL}███${R}${GRILL2}▓▒${R}`,
|
|
97
|
+
` ${GRILL2}▓${R}${GRILL}████${R} ${DIM}▒▒▒▒▒▒▒▒${R} ${GRILL}████${R}${GRILL2}▓${R}`,
|
|
98
|
+
` ${GRILL}▓███${R}${GRILL2}▒${R} ${PELT}██${R}${DIM}▒${R}${PELT}████${R}${DIM}▒${R}${PELT}██${R} ${GRILL2}▒${R}${GRILL}███▓${R}`,
|
|
99
|
+
` ${GRILL}████${R} ${PELT}███${R}${DIM}▒▒▒▒${R}${PELT}███${R} ${GRILL}████${R}`,
|
|
100
|
+
` ${GRILL}████${R}${GRILL2}▒${R} ${PELT}██████████${R} ${GRILL2}▒${R}${GRILL}████${R}`,
|
|
101
|
+
` ${GRILL}████${R} ${DIM}▒▒▒▒▒▒▒▒▒▒${R} ${GRILL}████${R}`,
|
|
102
|
+
` ${GRILL2}▒${R}${GRILL}████${R}${GRILL2}▓▓▓${R}${GRILL}████████████${R}${GRILL2}▓▓▓${R}${GRILL}████${R}${GRILL2}▒${R}`,
|
|
103
|
+
` ${GRILL}██████████████████████████${R}`,
|
|
104
|
+
` ${DIM}▒▒${R}${GRILL}████████${R}${GRILL2}▒▒▒▒▒▒▒▒▒▒▒▒${R}${GRILL}████████${R}${DIM}▒▒${R}`,
|
|
105
|
+
` ${PELT}▓${R}${GRILL}██████████${R}${DIM}░░░░░░░░░░░░░░${R}${GRILL}██████████${R}${PELT}▓${R}`,
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function titleBoxLines(C) {
|
|
110
|
+
const { R, B, TEAL, AMBER } = C;
|
|
111
|
+
const wide = 26;
|
|
112
|
+
const h = `${TEAL}═${R}`;
|
|
113
|
+
return [
|
|
114
|
+
`${TEAL}╔${R}${h.repeat(wide)}${TEAL}╗${R}`,
|
|
115
|
+
`${TEAL}║${R} ${B}${AMBER}INNERANIMAL MEDIA${R} ${TEAL}║${R}`,
|
|
116
|
+
`${TEAL}║${R} ${TEAL}inneranimalmedia.com${R} ${TEAL}║${R}`,
|
|
117
|
+
`${TEAL}╚${R}${h.repeat(wide)}${TEAL}╝${R}`,
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function ladderRows(C) {
|
|
122
|
+
const { R, TEAL } = C;
|
|
123
|
+
return [
|
|
124
|
+
` ${TEAL}║${R} ${TEAL}║${R}`,
|
|
125
|
+
` ${TEAL}╠═══╣${R}`,
|
|
126
|
+
` ${TEAL}║${R} ${TEAL}║${R}`,
|
|
127
|
+
` ${TEAL}╠═══╣${R}`,
|
|
128
|
+
` ${TEAL}║${R} ${TEAL}║${R}`,
|
|
129
|
+
` ${TEAL}╠═══╣${R}`,
|
|
130
|
+
` ${TEAL}║${R} ${TEAL}║${R}`,
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
// HUD RENDER
|
|
136
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
const HUD_ITEMS = [
|
|
139
|
+
{ key: 'workspace', icon: '⊞', label: 'Workspace' },
|
|
140
|
+
{ key: 'runtime', icon: '▣', label: 'Runtime' },
|
|
141
|
+
{ key: 'tunnel', icon: '⟁', label: 'Tunnel' },
|
|
142
|
+
{ key: 'agent', icon: '⬡', label: 'Agent' },
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const READY_LABELS = {
|
|
146
|
+
workspace: 'active',
|
|
147
|
+
runtime: 'ready',
|
|
148
|
+
tunnel: 'connected',
|
|
149
|
+
agent: 'online',
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
function renderHUDLine(states, C) {
|
|
153
|
+
const { R, B, TEAL, GHOST, WHT, DIM } = C;
|
|
154
|
+
return HUD_ITEMS.map((item, i) => {
|
|
155
|
+
const state = states[item.key] || 'checking';
|
|
156
|
+
let stateStr;
|
|
157
|
+
if (state === 'ready') {
|
|
158
|
+
stateStr = `${TEAL}● ${READY_LABELS[item.key]}${R}`;
|
|
159
|
+
} else if (state === 'checking') {
|
|
160
|
+
stateStr = `${GHOST}◌ checking...${R}`;
|
|
161
|
+
} else if (state === 'error') {
|
|
162
|
+
stateStr = `${X.fg('#ef4444')}✗ error${R}`;
|
|
163
|
+
} else {
|
|
164
|
+
stateStr = `${X.fg('#666666')}○ offline${R}`;
|
|
165
|
+
}
|
|
166
|
+
const div = i < HUD_ITEMS.length - 1 ? ` ${DIM}│${R} ` : '';
|
|
167
|
+
return `${GHOST}${item.icon}${R} ${B}${WHT}${item.label}${R} ${stateStr}${div}`;
|
|
168
|
+
}).join('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
+
// MAIN XTERM SPLASH
|
|
173
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @param {import('@xterm/xterm').Terminal} term
|
|
177
|
+
* @param {object} opts
|
|
178
|
+
* @param {string} opts.apiBase
|
|
179
|
+
* @param {string} opts.workspaceId
|
|
180
|
+
* @param {string} opts.authToken
|
|
181
|
+
* @param {'user_hosted_tunnel'|'platform_vm'|'sandbox'} opts.targetType
|
|
182
|
+
* @param {boolean} opts.skipArt
|
|
183
|
+
* @returns {Promise<() => void>} stopFlicker — call on unmount
|
|
184
|
+
*/
|
|
185
|
+
export async function runXtermSplash(term, {
|
|
186
|
+
apiBase = '',
|
|
187
|
+
workspaceId = '',
|
|
188
|
+
authToken = '',
|
|
189
|
+
targetType = 'platform_vm',
|
|
190
|
+
skipArt = false,
|
|
191
|
+
} = {}) {
|
|
192
|
+
|
|
193
|
+
const lane = getLaneTheme(targetType);
|
|
194
|
+
const C = buildColors();
|
|
195
|
+
const { R, B, TEAL, AMBER, GHOST, WHT, DIM, STONE, STONE2, FIRE1, FIRE2 } = C;
|
|
196
|
+
const PRIMARY = X.fg(lane.colors.primary);
|
|
197
|
+
const W = term.cols || 80;
|
|
198
|
+
|
|
199
|
+
const w = (s) => term.write(s);
|
|
200
|
+
const wl = (s = '') => term.write(s + '\r\n');
|
|
201
|
+
|
|
202
|
+
w(X.hide);
|
|
203
|
+
w(`${ESC}c`); // full reset
|
|
204
|
+
await sleep(60);
|
|
205
|
+
|
|
206
|
+
// cd verb flash
|
|
207
|
+
w(` ${GHOST}$ ${R}${AMBER}cd ~/inneranimalmedia${R}`);
|
|
208
|
+
await sleep(340);
|
|
209
|
+
w(`\r${X.clearLine}`);
|
|
210
|
+
await sleep(60);
|
|
211
|
+
|
|
212
|
+
if (!skipArt) {
|
|
213
|
+
wl();
|
|
214
|
+
|
|
215
|
+
const rows = gorillaRows(C);
|
|
216
|
+
const title = titleBoxLines(C);
|
|
217
|
+
const TITLE_START = 3;
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < rows.length; i++) {
|
|
220
|
+
const titleIdx = i - TITLE_START;
|
|
221
|
+
const titlePart = (titleIdx >= 0 && titleIdx < title.length)
|
|
222
|
+
? ' ' + title[titleIdx]
|
|
223
|
+
: '';
|
|
224
|
+
wl(` ${rows[i]}${titlePart}`);
|
|
225
|
+
await sleep(24);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
wl();
|
|
229
|
+
|
|
230
|
+
// Torches
|
|
231
|
+
w(` ${FIRE1} ▓▓▓${R}`);
|
|
232
|
+
await sleep(90);
|
|
233
|
+
wl(`${' '.repeat(Math.max(0, W - 14))}${FIRE2}▓▓▓${R}`);
|
|
234
|
+
await sleep(30);
|
|
235
|
+
|
|
236
|
+
// Ledge
|
|
237
|
+
w(' ');
|
|
238
|
+
for (let i = 0; i < W - 4; i++) {
|
|
239
|
+
w((i % 2 === 0) ? `${STONE}█${R}` : `${STONE2}▓${R}`);
|
|
240
|
+
await sleep(4);
|
|
241
|
+
}
|
|
242
|
+
wl();
|
|
243
|
+
wl(` ${DIM}${'╌'.repeat(W - 4)}${R}`);
|
|
244
|
+
await sleep(50);
|
|
245
|
+
|
|
246
|
+
// Ladder
|
|
247
|
+
for (const rung of ladderRows(C)) {
|
|
248
|
+
wl(rung);
|
|
249
|
+
await sleep(30);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
wl();
|
|
254
|
+
|
|
255
|
+
// Start prompt
|
|
256
|
+
await sleep(skipArt ? 0 : 70);
|
|
257
|
+
wl(` ${B}${AMBER}Start ${PRIMARY}▸${R}`);
|
|
258
|
+
await sleep(40);
|
|
259
|
+
wl(` ${GHOST}Type a command to begin.${R}`);
|
|
260
|
+
await sleep(skipArt ? 0 : 70);
|
|
261
|
+
wl();
|
|
262
|
+
|
|
263
|
+
// HUD — fire probes in parallel
|
|
264
|
+
const probePromise = probeAll({ apiBase, workspaceId, authToken });
|
|
265
|
+
const initStates = { workspace: 'checking', runtime: 'checking', tunnel: 'checking', agent: 'checking' };
|
|
266
|
+
|
|
267
|
+
wl(` ${DIM}${'─'.repeat(W - 4)}${R}`);
|
|
268
|
+
wl();
|
|
269
|
+
w(` ${renderHUDLine(initStates, C)}`);
|
|
270
|
+
wl(); wl();
|
|
271
|
+
wl(` ${DIM}${'─'.repeat(W - 4)}${R}`);
|
|
272
|
+
wl();
|
|
273
|
+
|
|
274
|
+
// Update HUD in-place when probes resolve
|
|
275
|
+
const LINES_BELOW = 4;
|
|
276
|
+
probePromise.then(resolved => {
|
|
277
|
+
w(X.up(LINES_BELOW));
|
|
278
|
+
w(`\r${X.clearLine}`);
|
|
279
|
+
w(` ${renderHUDLine(resolved, C)}`);
|
|
280
|
+
w('\r\n'.repeat(LINES_BELOW));
|
|
281
|
+
w(X.show);
|
|
282
|
+
w(` ${PRIMARY}>${R} `);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Torch flicker is handled via CSS animation on the panel wrapper
|
|
286
|
+
// (terminal-lanes.css) — xterm.js decoration API in Phase 2.
|
|
287
|
+
return () => {};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export default { runXtermSplash };
|