horizon-code 0.1.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/assets/python/highlights.scm +137 -0
- package/assets/python/tree-sitter-python.wasm +0 -0
- package/bin/horizon.js +2 -0
- package/package.json +40 -0
- package/src/ai/client.ts +369 -0
- package/src/ai/system-prompt.ts +86 -0
- package/src/app.ts +1454 -0
- package/src/chat/messages.ts +48 -0
- package/src/chat/renderer.ts +243 -0
- package/src/chat/types.ts +18 -0
- package/src/components/code-panel.ts +329 -0
- package/src/components/footer.ts +72 -0
- package/src/components/hooks-panel.ts +224 -0
- package/src/components/input-bar.ts +193 -0
- package/src/components/mode-bar.ts +245 -0
- package/src/components/session-panel.ts +294 -0
- package/src/components/settings-panel.ts +372 -0
- package/src/components/splash.ts +156 -0
- package/src/components/strategy-panel.ts +489 -0
- package/src/components/tab-bar.ts +112 -0
- package/src/components/tutorial-panel.ts +680 -0
- package/src/components/widgets/progress-bar.ts +38 -0
- package/src/components/widgets/sparkline.ts +57 -0
- package/src/hooks/executor.ts +109 -0
- package/src/index.ts +22 -0
- package/src/keys/handler.ts +198 -0
- package/src/platform/auth.ts +36 -0
- package/src/platform/client.ts +159 -0
- package/src/platform/config.ts +121 -0
- package/src/platform/session-sync.ts +158 -0
- package/src/platform/supabase.ts +376 -0
- package/src/platform/sync.ts +149 -0
- package/src/platform/tiers.ts +103 -0
- package/src/platform/tools.ts +163 -0
- package/src/platform/types.ts +86 -0
- package/src/platform/usage.ts +224 -0
- package/src/research/apis.ts +367 -0
- package/src/research/tools.ts +205 -0
- package/src/research/widgets.ts +523 -0
- package/src/state/store.ts +256 -0
- package/src/state/types.ts +109 -0
- package/src/strategy/ascii-chart.ts +74 -0
- package/src/strategy/code-stream.ts +146 -0
- package/src/strategy/dashboard.ts +140 -0
- package/src/strategy/persistence.ts +82 -0
- package/src/strategy/prompts.ts +626 -0
- package/src/strategy/sandbox.ts +137 -0
- package/src/strategy/tools.ts +764 -0
- package/src/strategy/validator.ts +216 -0
- package/src/strategy/widgets.ts +270 -0
- package/src/syntax/setup.ts +54 -0
- package/src/theme/colors.ts +107 -0
- package/src/theme/icons.ts +27 -0
- package/src/util/hyperlink.ts +21 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Sandbox for isolated Python execution
|
|
2
|
+
// Runs strategy code in a temp directory with stripped environment
|
|
3
|
+
|
|
4
|
+
import { mkdtemp, writeFile, rm } from "fs/promises";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { mkdirSync as mkdirSyncFs, existsSync } from "fs";
|
|
8
|
+
import { listEncryptedEnvNames, getDecryptedEnv } from "../platform/config.ts";
|
|
9
|
+
|
|
10
|
+
interface SandboxResult {
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
exitCode: number | null;
|
|
14
|
+
timedOut: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface SandboxOptions {
|
|
18
|
+
code: string;
|
|
19
|
+
timeout?: number; // ms, default 30000
|
|
20
|
+
cwd?: string; // override temp dir
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Minimal env for backtesting — NO secrets, NO exchange keys
|
|
24
|
+
// Used by runInSandbox (backtest, validation)
|
|
25
|
+
function backtestEnv(): Record<string, string> {
|
|
26
|
+
const env: Record<string, string> = {};
|
|
27
|
+
const allow = ["PATH", "HOME", "LANG", "LC_ALL", "TERM", "PYTHONPATH", "VIRTUAL_ENV", "CONDA_PREFIX"];
|
|
28
|
+
for (const key of allow) {
|
|
29
|
+
if (process.env[key]) env[key] = process.env[key]!;
|
|
30
|
+
}
|
|
31
|
+
env.PYTHONUNBUFFERED = "1";
|
|
32
|
+
env.PYTHONDONTWRITEBYTECODE = "1";
|
|
33
|
+
return env;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Full env for live/paper execution — includes exchange keys
|
|
37
|
+
// Used by spawnInSandbox (run_strategy) — user has explicitly chosen to run
|
|
38
|
+
function liveEnv(): Record<string, string> {
|
|
39
|
+
const env = backtestEnv();
|
|
40
|
+
// Add SDK keys needed for hz.run() to connect to exchanges
|
|
41
|
+
const secrets = ["HORIZON_API_KEY", "HORIZON_PRIVATE_KEY", "POLYMARKET_PRIVATE_KEY", "PK", "CLOB_API_KEY", "CLOB_SECRET", "CLOB_PASSPHRASE"];
|
|
42
|
+
for (const key of secrets) {
|
|
43
|
+
if (process.env[key]) env[key] = process.env[key]!;
|
|
44
|
+
}
|
|
45
|
+
// Pass HORIZON_ prefixed vars
|
|
46
|
+
for (const [key, val] of Object.entries(process.env)) {
|
|
47
|
+
if (key.startsWith("HORIZON_") && val && !env[key]) env[key] = val;
|
|
48
|
+
}
|
|
49
|
+
// Inject encrypted env vars from /env command
|
|
50
|
+
try {
|
|
51
|
+
for (const name of listEncryptedEnvNames()) {
|
|
52
|
+
if (!env[name]) {
|
|
53
|
+
const val = getDecryptedEnv(name);
|
|
54
|
+
if (val) env[name] = val;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
return env;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run Python code in an isolated sandbox.
|
|
63
|
+
* - Creates a temp directory
|
|
64
|
+
* - Writes code to a file (avoids shell escaping issues with -c)
|
|
65
|
+
* - Runs with stripped environment
|
|
66
|
+
* - Enforces timeout
|
|
67
|
+
* - Cleans up temp directory
|
|
68
|
+
*/
|
|
69
|
+
export async function runInSandbox(opts: SandboxOptions): Promise<SandboxResult> {
|
|
70
|
+
const timeout = opts.timeout ?? 30000;
|
|
71
|
+
const dir = opts.cwd ?? await mkdtemp(join(tmpdir(), "horizon-sandbox-"));
|
|
72
|
+
const scriptPath = join(dir, "strategy.py");
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await writeFile(scriptPath, opts.code, "utf-8");
|
|
76
|
+
|
|
77
|
+
const pythonPath = process.env.HORIZON_PYTHON ?? process.env.PYTHON_PATH ?? "python3";
|
|
78
|
+
const proc = Bun.spawn([pythonPath, "-u", scriptPath], {
|
|
79
|
+
stdout: "pipe",
|
|
80
|
+
stderr: "pipe",
|
|
81
|
+
cwd: dir,
|
|
82
|
+
env: backtestEnv(), // No secrets — backtesting doesn't need exchange keys
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
let timedOut = false;
|
|
86
|
+
const timer = setTimeout(() => {
|
|
87
|
+
timedOut = true;
|
|
88
|
+
proc.kill();
|
|
89
|
+
}, timeout);
|
|
90
|
+
|
|
91
|
+
const stdout = await new Response(proc.stdout).text();
|
|
92
|
+
const stderr = await new Response(proc.stderr).text();
|
|
93
|
+
await proc.exited;
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
|
|
96
|
+
return { stdout, stderr, exitCode: proc.exitCode, timedOut };
|
|
97
|
+
} finally {
|
|
98
|
+
// Clean up temp directory
|
|
99
|
+
if (!opts.cwd) {
|
|
100
|
+
rm(dir, { recursive: true, force: true }).catch(() => {});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Run Python code as a background process in sandbox.
|
|
107
|
+
* Returns the process and temp dir for cleanup.
|
|
108
|
+
*/
|
|
109
|
+
export function spawnInSandbox(code: string, dir?: string): {
|
|
110
|
+
proc: ReturnType<typeof Bun.spawn>;
|
|
111
|
+
dir: string;
|
|
112
|
+
cleanup: () => Promise<void>;
|
|
113
|
+
} {
|
|
114
|
+
const sandboxDir = dir ?? join(tmpdir(), `horizon-sandbox-${Date.now()}`);
|
|
115
|
+
if (!existsSync(sandboxDir)) mkdirSyncFs(sandboxDir, { recursive: true });
|
|
116
|
+
const scriptPath = join(sandboxDir, "strategy.py");
|
|
117
|
+
|
|
118
|
+
// Sync write since we need it before spawn
|
|
119
|
+
Bun.write(scriptPath, code);
|
|
120
|
+
|
|
121
|
+
const pythonPath = process.env.HORIZON_PYTHON ?? process.env.PYTHON_PATH ?? "python3";
|
|
122
|
+
const proc = Bun.spawn([pythonPath, "-u", scriptPath], {
|
|
123
|
+
stdout: "pipe",
|
|
124
|
+
stderr: "pipe",
|
|
125
|
+
cwd: sandboxDir,
|
|
126
|
+
env: liveEnv(), // Live execution — includes exchange keys for hz.run()
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
proc,
|
|
131
|
+
dir: sandboxDir,
|
|
132
|
+
cleanup: async () => {
|
|
133
|
+
try { proc.kill(); } catch {}
|
|
134
|
+
rm(sandboxDir, { recursive: true, force: true }).catch(() => {});
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|