@inneranimalmedia/agentsam-sdk 2.4.0 → 2.5.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.
Files changed (162) hide show
  1. package/README.md +7 -6
  2. package/docs/AGENTSAM_WORKBENCH.md +30 -0
  3. package/docs/AUTH_IDENTITY_CONTRACT.md +52 -0
  4. package/docs/CAPABILITIES.md +5 -3
  5. package/docs/CLI_SHELL.md +57 -80
  6. package/docs/CMS_STUDIO.md +101 -0
  7. package/docs/CONTEXT.md +170 -0
  8. package/docs/LOCAL_OLLAMA.md +58 -0
  9. package/docs/MERKLE.md +19 -19
  10. package/docs/PORTABLE_CONTEXT.md +4 -3
  11. package/docs/PROJECT_CONFIG.md +72 -0
  12. package/docs/RELEASES.md +26 -2
  13. package/docs/REPOSITORY_INTELLIGENCE.md +1 -1
  14. package/docs/REPOSITORY_KNOWLEDGE.md +114 -0
  15. package/docs/SDK_WORKER.md +86 -0
  16. package/docs/SECURITY.md +59 -22
  17. package/docs/client-cms-editor.md +15 -0
  18. package/docs/local-studio/WORKMODE_DONOR_NOTES.md +485 -0
  19. package/package.json +17 -5
  20. package/packages/identity/package.json +11 -2
  21. package/packages/identity/src/contracts/auth-config.js +98 -0
  22. package/packages/identity/src/index.js +1 -0
  23. package/packages/identity/src/oauth/README.md +2 -2
  24. package/packages/identity/src/oauth/credentials.js +11 -4
  25. package/packages/identity/src/oauth/iam-platform.js +3 -3
  26. package/packages/identity/src/providers/iam/index.js +7 -7
  27. package/packages/identity/src/providers/iam/oauth.js +6 -4
  28. package/packages/identity/src/providers/iam/profile.js +5 -5
  29. package/packages/identity/tests/auth-config.test.mjs +57 -0
  30. package/packages/identity/tests/oauth-credentials.test.mjs +13 -2
  31. package/protocol/FILEMETA_V1.md +95 -0
  32. package/protocol/INSPECT_VIEWS_V1.md +40 -0
  33. package/protocol/MERKLE_PERSISTENCE_V1.md +50 -0
  34. package/protocol/MERKLE_V1.md +3 -1
  35. package/protocol/capabilities/manifest.json +32 -3
  36. package/protocol/capabilities/repository-snapshot.schema.json +1 -0
  37. package/protocol/context/context-budget.schema.json +41 -0
  38. package/protocol/context/context-item.schema.json +20 -0
  39. package/protocol/context/resolved-context-pack.schema.json +42 -0
  40. package/protocol/context/result-policy.schema.json +17 -0
  41. package/protocol/knowledge/context-pack.schema.json +33 -0
  42. package/protocol/knowledge/retrieval-query.schema.json +68 -13
  43. package/python/README.md +15 -7
  44. package/python/agentsam_sdk/cli.py +0 -21
  45. package/python/agentsam_sdk/tui/README.md +17 -12
  46. package/python/agentsam_sdk/tui/bootstrap.py +2 -2
  47. package/python/agentsam_sdk/tui/demo.py +25 -10
  48. package/python/agentsam_sdk/tui/onboarding.py +208 -0
  49. package/python/tests/test_tui_cli.py +7 -6
  50. package/skills/README.md +21 -0
  51. package/skills/agentsam-app-fundamentals/SKILL.md +165 -0
  52. package/skills/agentsam-app-fundamentals/references/graphs-contracts-ast-merkle.md +89 -0
  53. package/skills/agentsam-app-fundamentals/references/trust-credentials-and-destinations.md +99 -0
  54. package/skills/agentsam-jr-dev/SKILL.md +232 -0
  55. package/skills/agentsam-jr-dev/references/real-application-logic.md +156 -0
  56. package/skills/agentsam-jr-dev/references/web-application-fundamentals.md +240 -0
  57. package/skills/agentsam-progression-guard/SKILL.md +197 -0
  58. package/skills/agentsam-progression-guard/references/checkpoint-chain.md +111 -0
  59. package/skills/agentsam-progression-guard/references/hooks-operational-io.md +96 -0
  60. package/skills/catalog.json +53 -0
  61. package/src/capabilities/index.js +7 -0
  62. package/src/capabilities/repository-snapshot-view.js +238 -0
  63. package/src/capabilities/repository-snapshot.js +28 -14
  64. package/src/cli.js +108 -56
  65. package/src/commands/cad.js +56 -6
  66. package/src/commands/context.js +14 -2
  67. package/src/commands/db.js +4 -7
  68. package/src/commands/deploy.js +17 -31
  69. package/src/commands/interactive.js +21 -0
  70. package/src/commands/knowledge.js +27 -4
  71. package/src/commands/merkle-persist.js +118 -0
  72. package/src/commands/merkle.js +32 -17
  73. package/src/commands/models.js +107 -0
  74. package/src/commands/ollama.js +259 -0
  75. package/src/commands/preferences.js +102 -0
  76. package/src/commands/product.js +86 -16
  77. package/src/commands/security.js +3 -3
  78. package/src/commands/shell.js +71 -27
  79. package/src/commands/skills.js +66 -0
  80. package/src/commands/start-local.js +1 -1
  81. package/src/commands/tunnel.js +5 -4
  82. package/src/context/budget.js +58 -0
  83. package/src/context/compact.js +28 -0
  84. package/src/context/index.js +5 -0
  85. package/src/context/resolve.js +84 -0
  86. package/src/context/result-policy.js +66 -0
  87. package/src/index.js +16 -0
  88. package/src/indexing/execution-boundary.js +144 -0
  89. package/src/indexing/index.js +8 -0
  90. package/src/indexing/provider.js +41 -0
  91. package/src/knowledge/config.js +2 -2
  92. package/src/knowledge/context-pack.js +12 -1
  93. package/src/knowledge/contracts.js +9 -4
  94. package/src/knowledge/engine.js +7 -3
  95. package/src/knowledge/service/server.js +1 -1
  96. package/src/lib/auth.js +10 -19
  97. package/src/lib/bridge-client.js +7 -5
  98. package/src/lib/cli-preferences.js +74 -0
  99. package/src/lib/core-client.js +8 -8
  100. package/src/lib/deploy-receipt/index.js +5 -2
  101. package/src/lib/detect-context.js +6 -5
  102. package/src/lib/identity-scaffold.js +1 -1
  103. package/src/lib/local-scaffold.js +35 -33
  104. package/src/lib/local-status.js +9 -17
  105. package/src/lib/merkle/cloudflare-persistence.js +321 -0
  106. package/src/lib/merkle/filemeta.js +43 -0
  107. package/src/lib/merkle/git-ignore.js +24 -0
  108. package/src/lib/merkle/hash.js +1 -0
  109. package/src/lib/merkle/index.js +22 -0
  110. package/src/lib/merkle/persistence.js +72 -0
  111. package/src/lib/merkle/semantic.js +359 -0
  112. package/src/lib/merkle/snapshot.js +9 -3
  113. package/src/lib/merkle/tree.js +11 -6
  114. package/src/lib/open-url.js +66 -0
  115. package/src/lib/project-config.js +227 -0
  116. package/src/lib/project-rules.js +68 -0
  117. package/src/lib/save-sdk-token.js +1 -1
  118. package/src/lib/slash-commands.js +2 -1
  119. package/src/lib/tools.js +11 -5
  120. package/src/security/index.js +1 -0
  121. package/src/security/inventory.js +4 -1
  122. package/src/security/render.js +27 -5
  123. package/src/security/scan.js +24 -9
  124. package/src/security/trust-boundary.js +24 -0
  125. package/src/skills/index.js +64 -0
  126. package/src/tools/index.js +1 -0
  127. package/src/tools/search.js +70 -0
  128. package/src/ui/ansi.js +1 -1
  129. package/src/ui/boot.js +56 -0
  130. package/src/ui/merkle/render.js +1 -0
  131. package/src/ui/runtime-activity.js +192 -0
  132. package/src/ui/theme.js +19 -18
  133. package/test/app-building-skills.test.mjs +61 -0
  134. package/test/apps-scaffold-contract.test.mjs +56 -0
  135. package/test/capabilities.test.mjs +53 -4
  136. package/test/cli-preferences.test.mjs +25 -0
  137. package/test/context.test.mjs +95 -0
  138. package/test/indexing-provider.test.mjs +29 -0
  139. package/test/jr-dev-skill.test.mjs +26 -0
  140. package/test/knowledge-context-pack.test.mjs +19 -0
  141. package/test/knowledge.test.mjs +1 -1
  142. package/test/merkle-persistence.test.mjs +91 -0
  143. package/test/merkle.test.mjs +77 -3
  144. package/test/models.test.mjs +37 -0
  145. package/test/ollama.test.mjs +94 -0
  146. package/test/open-url.test.mjs +64 -0
  147. package/test/project-config.test.mjs +81 -0
  148. package/test/project-rules.test.mjs +44 -0
  149. package/test/release-hygiene.test.mjs +34 -0
  150. package/test/repository-snapshot-view.test.mjs +112 -0
  151. package/test/runtime-activity.test.mjs +98 -0
  152. package/test/sdk-worker-contract.test.mjs +68 -0
  153. package/test/security.test.mjs +46 -0
  154. package/test/shell.test.mjs +8 -1
  155. package/test/skills.test.mjs +22 -0
  156. package/test/smoke.mjs +2 -2
  157. package/test/theme-portability.test.mjs +14 -0
  158. package/test/tools-search.test.mjs +27 -0
  159. package/examples/agentsam-tui-ansi.mjs +0 -149
  160. package/src/commands/tui.js +0 -120
  161. package/src/ui/splash-xterm.js +0 -290
  162. package/src/ui/splash.js +0 -426
@@ -1,149 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Zero-dependency ANSI companion to the Agent Sam SDK TUI.
4
- * Use this from Agent Sam PTY / Node scripts when you do not want Python+Rich.
5
- *
6
- * node scripts/demo_agentsam_ansi.mjs
7
- * node scripts/demo_agentsam_ansi.mjs --scene sprite
8
- * node scripts/demo_agentsam_ansi.mjs --check
9
- */
10
-
11
- const HIDE = "\x1b[?25l";
12
- const SHOW = "\x1b[?25h";
13
- const CLEAR_LINE = "\x1b[2K";
14
- const MAGENTA = "\x1b[38;2;211;54;130m";
15
- const CYAN = "\x1b[38;2;45;212;191m";
16
- const MUTED = "\x1b[38;2;122;154;170m";
17
- const GREEN = "\x1b[38;2;163;184;0m";
18
- const RESET = "\x1b[0m";
19
- const BOLD = "\x1b[1m";
20
- const DIM = "\x1b[2m";
21
-
22
- const WALKER = [
23
- [" · ", " ○ ", " │╲ ", " │ ○ ", " ○ ░ ", " ░ "],
24
- [" · ", " ○ ", " │╲ ", " ○ ○ ", " ░ ", " ░ "],
25
- [" · ", " ○ ", " │╲ ", " ○ │ ", " ○ ", " ░ "],
26
- [" · ", " ○ ", " ╱│ ", " ○ │ ", " ○ ", " ░ "],
27
- [" · ", " ○ ", " ╱│ ", " ○ │ ", " ○ ░ ", " ░ "],
28
- [" · ", " ○ ", " ╱│ ", " ○ │ ", " ○ ░ ", " ░ "],
29
- [" · ", " ○ ", " │╲ ", " ○ ○ ", " ○ ░ ", " ░ "],
30
- [" · ", " ○ ", " │╲ ", " │ ○ ", " ○ ░ ", " ░ "],
31
- ];
32
-
33
- const BRAILLE = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
34
-
35
- function sleep(ms) {
36
- return new Promise((resolve) => setTimeout(resolve, ms));
37
- }
38
-
39
- function paint(lines) {
40
- const payload = lines.map((line) => `${CLEAR_LINE}${line}`).join("\n");
41
- process.stdout.write(`${payload}\n`);
42
- return lines.length;
43
- }
44
-
45
- function moveUp(n) {
46
- if (n > 0) process.stdout.write(`\x1b[${n}A`);
47
- }
48
-
49
- function box(title, bodyLines, width = 42) {
50
- const inner = width - 2;
51
- const top = `${MAGENTA}╭${"─".repeat(inner)}╮${RESET}`;
52
- const label = ` ${title} `;
53
- const titled = `${MAGENTA}╭${RESET}${BOLD}${CYAN}${label}${RESET}${MAGENTA}${"─".repeat(Math.max(0, inner - label.length))}╮${RESET}`;
54
- const mid = bodyLines.map((line) => {
55
- const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
56
- const pad = Math.max(0, inner - 2 - visible.length);
57
- return `${MAGENTA}│${RESET} ${line}${" ".repeat(pad)} ${MAGENTA}│${RESET}`;
58
- });
59
- const bot = `${MAGENTA}╰${"─".repeat(inner)}╯${RESET}`;
60
- return [titled.length ? titled : top, ...mid, bot];
61
- }
62
-
63
- async function sceneSprite({ ticks, delay }) {
64
- let used = 0;
65
- for (let i = 0; i < ticks; i += 1) {
66
- const frame = WALKER[i % WALKER.length];
67
- const caption = `${MUTED}Agent Sam is walking · frame ${String(i + 1).padStart(2, "0")}${RESET}`;
68
- const lines = box("agent activity", [...frame.map((row) => `${CYAN}${BOLD}${row}${RESET}`), "", caption], 34);
69
- if (used) moveUp(used);
70
- used = paint(lines);
71
- await sleep(delay);
72
- }
73
- return used;
74
- }
75
-
76
- async function sceneDashboard({ ticks, delay }) {
77
- const total = 2320;
78
- let used = 0;
79
- const started = Date.now();
80
- for (let i = 0; i < ticks; i += 1) {
81
- const done = Math.min(total, Math.round(((i + 1) / ticks) * total));
82
- const symbols = Math.round(done * 2.76);
83
- const elapsed = Math.round((Date.now() - started) / 1000);
84
- const spin = BRAILLE[i % BRAILLE.length];
85
- const walker = WALKER[i % WALKER.length];
86
- const pct = Math.round((done / total) * 100);
87
- const barFilled = Math.round(pct / 5);
88
- const bar = `${CYAN}${"█".repeat(barFilled)}${MUTED}${"░".repeat(20 - barFilled)}${RESET}`;
89
- const body = [
90
- `${MUTED}stage${RESET} ${CYAN}${BOLD}parse_chunks${RESET} ${spin}`,
91
- `${MUTED}files${RESET} ${done.toLocaleString()} / ${total.toLocaleString()}`,
92
- `${MUTED}symbols${RESET} ${symbols.toLocaleString()}`,
93
- `${MUTED}errors${RESET} ${GREEN}0${RESET}`,
94
- `${MUTED}elapsed${RESET} ${elapsed}s`,
95
- `${bar} ${pct}%`,
96
- "",
97
- ...walker.map((row) => `${CYAN}${row}${RESET}`),
98
- ];
99
- const lines = box("◉ Code Index", body, 46);
100
- if (used) moveUp(used);
101
- used = paint(lines);
102
- await sleep(delay);
103
- }
104
- return used;
105
- }
106
-
107
- async function sceneLogs({ delay }) {
108
- const rows = [
109
- ["INFO", "Connected to Hyperdrive", "471 ms"],
110
- ["WORK", "Parsing batch", "24 files"],
111
- ["OK", "Checkpoint committed", "durable"],
112
- ];
113
- const styles = { INFO: "\x1b[38;2;58;159;232m", WORK: "\x1b[38;2;230;172;0m", OK: GREEN };
114
- for (const [level, message, detail] of rows) {
115
- const now = new Date().toISOString().slice(11, 19);
116
- process.stdout.write(
117
- `${DIM}${now}${RESET} ${styles[level]}${level.padStart(4)}${RESET} ${message} ${DIM}· ${detail}${RESET}\n`,
118
- );
119
- await sleep(delay);
120
- }
121
- return 0;
122
- }
123
-
124
- async function main() {
125
- const args = new Set(process.argv.slice(2));
126
- const check = args.has("--check");
127
- const scene = [...args].find((a) => !a.startsWith("--")) || (args.has("--scene") ? null : "all");
128
- const sceneFlagIdx = process.argv.indexOf("--scene");
129
- const named = sceneFlagIdx >= 0 ? process.argv[sceneFlagIdx + 1] : scene;
130
- const which = named && named !== "all" && !named.startsWith("--") ? named : "all";
131
- const delay = check ? 0 : 90;
132
- const ticks = check ? 4 : 32;
133
-
134
- process.stdout.write(HIDE);
135
- try {
136
- if (which === "sprite" || which === "all") await sceneSprite({ ticks, delay });
137
- if (which === "dashboard" || which === "all") await sceneDashboard({ ticks, delay });
138
- if (which === "logs" || which === "all") await sceneLogs({ delay: check ? 0 : 120 });
139
- process.stdout.write(`\n${GREEN}${BOLD}Done.${RESET} Use the Python agentsam tui command for Rich layouts.\n`);
140
- } finally {
141
- process.stdout.write(SHOW);
142
- }
143
- }
144
-
145
- main().catch((err) => {
146
- process.stdout.write(SHOW);
147
- console.error(err);
148
- process.exit(1);
149
- });
@@ -1,120 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { spawn } from 'node:child_process';
4
- import { fileURLToPath } from 'node:url';
5
- import { collectLocalStatus } from '../lib/local-status.js';
6
- import { renderLocalStatus } from '../ui/ansi.js';
7
- import { runMerkle } from './merkle.js';
8
-
9
- const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
10
- const ANSI_DEMO = path.join(ROOT, 'examples', 'agentsam-tui-ansi.mjs');
11
- const PYTHON_ROOT = path.join(ROOT, 'python');
12
-
13
- function run(command, args, options = {}) {
14
- return new Promise((resolve, reject) => {
15
- const child = spawn(command, args, {
16
- stdio: options.stdio || 'inherit',
17
- cwd: options.cwd || process.cwd(),
18
- env: options.env || process.env,
19
- });
20
- child.once('error', reject);
21
- child.once('exit', (code, signal) => {
22
- if (signal) reject(new Error(`${command} stopped by ${signal}`));
23
- else if (code === 0) resolve(0);
24
- else reject(new Error(`${command} exited ${code ?? 1}`));
25
- });
26
- });
27
- }
28
-
29
- function hasScene(args) {
30
- return args.includes('--scene') || args.some((arg) => arg.startsWith('--scene='));
31
- }
32
-
33
- function venvPython(venvRoot) {
34
- return process.platform === 'win32'
35
- ? path.join(venvRoot, 'Scripts', 'python.exe')
36
- : path.join(venvRoot, 'bin', 'python');
37
- }
38
-
39
- async function findSystemPython() {
40
- for (const candidate of process.platform === 'win32' ? ['python', 'py'] : ['python3', 'python']) {
41
- try {
42
- await run(candidate, ['--version'], { stdio: 'ignore' });
43
- return candidate;
44
- } catch {
45
- // try next candidate
46
- }
47
- }
48
- return null;
49
- }
50
-
51
- async function pythonHasRich(python) {
52
- try {
53
- await run(python, ['-c', 'import rich'], { stdio: 'ignore' });
54
- return true;
55
- } catch {
56
- return false;
57
- }
58
- }
59
-
60
- async function runRich(args) {
61
- const install = args.includes('--install');
62
- const forwarded = args.filter((arg) => arg !== '--install');
63
- const projectVenv = path.resolve(process.cwd(), '.agentsam', 'tui-venv');
64
- let python = fs.existsSync(venvPython(projectVenv)) ? venvPython(projectVenv) : null;
65
-
66
- if (!python && install) {
67
- const systemPython = await findSystemPython();
68
- if (!systemPython) throw new Error('Python 3 is required for the optional Rich TUI.');
69
- fs.mkdirSync(path.dirname(projectVenv), { recursive: true });
70
- console.log(`\n Agent Sam Rich TUI setup\n · creating ${projectVenv}`);
71
- await run(systemPython, ['-m', 'venv', projectVenv]);
72
- python = venvPython(projectVenv);
73
- console.log(' · installing rich into isolated Agent Sam venv');
74
- await run(python, ['-m', 'pip', 'install', 'rich>=13.7.0']);
75
- }
76
-
77
- if (!python) {
78
- const systemPython = await findSystemPython();
79
- if (systemPython && (await pythonHasRich(systemPython))) python = systemPython;
80
- }
81
-
82
- if (!python) {
83
- throw new Error(
84
- 'Rich TUI is optional and not installed. Run `agentsam tui rich --install` to create an isolated .agentsam/tui-venv.',
85
- );
86
- }
87
-
88
- const env = {
89
- ...process.env,
90
- PYTHONPATH: [PYTHON_ROOT, process.env.PYTHONPATH].filter(Boolean).join(path.delimiter),
91
- };
92
-
93
- if (!hasScene(forwarded)) {
94
- const status = await collectLocalStatus();
95
- env.AGENTSAM_STATUS_JSON = JSON.stringify(status);
96
- await run(python, ['-m', 'agentsam_sdk.tui.status'], { env });
97
- return;
98
- }
99
-
100
- await run(python, ['-m', 'agentsam_sdk.tui', ...forwarded], { env });
101
- }
102
-
103
- export async function runTui(argv = []) {
104
- if (argv[0] === 'merkle') return runMerkle(['tui', ...argv.slice(1)]);
105
- const args = [...argv];
106
- const mode = args[0] === 'rich' || args[0] === 'ansi' ? args.shift() : 'ansi';
107
-
108
- if (mode === 'rich') {
109
- await runRich(args);
110
- return;
111
- }
112
-
113
- if (!hasScene(args)) {
114
- const status = await collectLocalStatus();
115
- process.stdout.write(`\n${renderLocalStatus(status)}\n\n`);
116
- return;
117
- }
118
-
119
- await run(process.execPath, [ANSI_DEMO, ...args]);
120
- }
@@ -1,290 +0,0 @@
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 };