File size: 5,787 Bytes
ee9fcd0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env node
/**
 * CLI entry point for @openhands/agent-canvas
 *
 * Runs the full Agent Canvas stack locally by default:
 * - Agent-server via uvx
 * - Automation backend via uvx
 * - Pre-built static frontend
 *
 * This is the production equivalent of `npm run dev` - it runs the full stack
 * but serves pre-built static assets instead of the Vite dev server.
 */

import { existsSync, readFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const PKG_JSON = join(__dirname, "..", "package.json");
// Build output is in build/ (not build/client/) - see react-router.config.ts unpackClientDirectory
const BUILD_DIR = join(__dirname, "..", "build");

const DEFAULTS_JSON = join(__dirname, "..", "config", "defaults.json");

// Check for version/help/info/public flags first
const args = process.argv.slice(2);
if (args.includes("-v") || args.includes("--version")) {
  const { version } = JSON.parse(readFileSync(PKG_JSON, "utf-8"));
  console.log(version);
  process.exit(0);
}
if (args.includes("--info")) {
  const { version } = JSON.parse(readFileSync(PKG_JSON, "utf-8"));
  const defaults = JSON.parse(readFileSync(DEFAULTS_JSON, "utf-8"));
  console.log(`@openhands/agent-canvas ${version}

Default stack versions:
  agent-server:    ${defaults.versions.agentServer}
  automation:      ${defaults.versions.automation}

Compatibility:
  agent-server:    >= ${defaults.compatibility.minimumAgentServer}

Default ports:
  ingress:         ${defaults.ports.proxy}
  agent-server:    ${defaults.ports.agentServer}
  automation:      ${defaults.ports.automation}

Override versions via environment variables:
  OH_AGENT_SERVER_VERSION, OH_AGENT_SERVER_GIT_REF, OH_AGENT_SERVER_LOCAL_PATH
  OH_AUTOMATION_VERSION, OH_AUTOMATION_GIT_REF`);
  process.exit(0);
}
const isPublic = args.includes("--public");
const isFrontendOnly = args.includes("--frontend-only");
const isBackendOnly = args.includes("--backend-only");

if (args.includes("-h") || args.includes("--help")) {
  console.log(`
@openhands/agent-canvas - Run the Agent Canvas UI with agent-server

Runs the full stack with agent-server and automation backend via uvx,
and serves pre-built static frontend assets.

USAGE:
  npx @openhands/agent-canvas [options]

AUTH MODES:
  By default the server auto-generates an API key that is injected into
  the frontend at startup — no login required.

  --public    Enable public mode. Requires LOCAL_BACKEND_API_KEY env var.
              The key protects the server but is NOT injected into the
              frontend. Users must paste it when the UI loads.

OPTIONS:
  -p, --port <port>     Ingress port (default: 8000)
  --public              Enable public mode (see above)
  --frontend-only       Start only the static frontend behind ingress
  --backend-only        Start only agent-server + automation behind ingress
  -v, --version         Show version number
  --info                Show version and default stack configuration
  -h, --help            Show this help message

ENVIRONMENT VARIABLES:
  LOCAL_BACKEND_API_KEY        API key for the server. Required in --public
                               mode; optional otherwise (auto-generated if
                               omitted, persisted across restarts).
  OH_SECRET_KEY                Secret key for encrypting settings
  OH_AGENT_SERVER_GIT_REF      Git ref for agent-server
  OH_AGENT_SERVER_LOCAL_PATH   Path to local SDK checkout (for development)
  OH_AGENT_SERVER_VERSION      Specific PyPI version for agent-server

Note: LLM settings are configured through the web UI settings page,
not environment variables.

EXAMPLES:
  # Start full stack (local mode, auto-generated key)
  npx @openhands/agent-canvas

  # Pin a specific key (local mode, key auto-injected into frontend)
  LOCAL_BACKEND_API_KEY=my-key npx @openhands/agent-canvas

  # Public mode — users must enter the API key in the browser
  LOCAL_BACKEND_API_KEY=my-secret npx @openhands/agent-canvas --public

  # Use a specific port
  npx @openhands/agent-canvas --port 3000

  # Start only the static frontend behind ingress
  npx @openhands/agent-canvas --frontend-only

  # Start only the agent-server and automation backend behind ingress
  npx @openhands/agent-canvas --backend-only

  # Show default stack versions and ports
  npx @openhands/agent-canvas --info

  # Use local SDK checkout for development
  OH_AGENT_SERVER_LOCAL_PATH=/path/to/sdk npx @openhands/agent-canvas
`);
  process.exit(0);
}

if (isFrontendOnly && isBackendOnly) {
  console.error(
    "Error: --frontend-only and --backend-only cannot be used together",
  );
  process.exit(1);
}

if (isFrontendOnly && isPublic) {
  console.error("Error: --public cannot be used with --frontend-only");
  process.exit(1);
}

// Check build exists before doing anything else unless no frontend will run.
if (!isBackendOnly && !existsSync(BUILD_DIR)) {
  console.error(`
Error: No build found at ${BUILD_DIR}

This package needs to be built first. If you installed from npm,
this is a packaging error. If running from source:

  npm install
  npm run build
`);
  process.exit(1);
}

let main;
try {
  ({ main } = await import("../scripts/dev-with-automation.mjs"));
} catch (err) {
  console.error("Failed to load required scripts. Try reinstalling:");
  console.error("  npm install -g @openhands/agent-canvas@latest");
  console.error(`\nError: ${err.message}`);
  process.exit(1);
}

main({
  bannerTitle: "Agent Canvas",
  staticMode: true,
  staticDir: BUILD_DIR,
  mode: "agent-canvas",
  isPublic,
}).catch((err) => {
  console.error(`Fatal error: ${err.message}`);
  if (err.stack) {
    console.error(err.stack);
  }
  process.exit(1);
});