@chrysb/alphaclaw 0.9.17 → 0.9.19
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 +28 -0
- package/bin/alphaclaw.js +28 -6
- package/lib/cli/git-runtime.js +41 -0
- package/lib/cli/openclaw-config-restore.js +1 -1
- package/lib/public/dist/app.bundle.js +1265 -1225
- package/lib/public/js/components/api-feature-panel.js +76 -0
- package/lib/public/js/components/general/index.js +6 -0
- package/lib/public/js/components/general/use-general-tab.js +69 -0
- package/lib/public/js/lib/api.js +19 -0
- package/lib/public/js/lib/storage-keys.js +4 -0
- package/lib/scripts/git +1 -1
- package/lib/server/agents/shared.js +18 -2
- package/lib/server/alphaclaw-config.js +99 -0
- package/lib/server/constants.js +48 -0
- package/lib/server/env.js +71 -20
- package/lib/server/gateway.js +175 -88
- package/lib/server/init/register-server-routes.js +8 -0
- package/lib/server/login-throttle.js +41 -22
- package/lib/server/onboarding/cron.js +8 -0
- package/lib/server/onboarding/openclaw.js +27 -3
- package/lib/server/openclaw-config-migrations.js +77 -0
- package/lib/server/openclaw-thinking.js +26 -1
- package/lib/server/routes/proxy.js +219 -1
- package/lib/server/routes/system.js +65 -0
- package/lib/server/usage-tracker-config.js +5 -1
- package/lib/server.js +35 -1
- package/lib/setup/hourly-git-sync.sh +17 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -157,7 +157,35 @@ The built-in watchdog monitors gateway health and recovers from failures automat
|
|
|
157
157
|
| `WATCHDOG_NOTIFICATIONS_DISABLED` | Optional | Disable watchdog notifications (`true`/`false`) |
|
|
158
158
|
| `PORT` | Optional | Server port (default `3000`) |
|
|
159
159
|
| `ALPHACLAW_ROOT_DIR` | Optional | Data directory (default `/data`) |
|
|
160
|
+
| `ALPHACLAW_SKIP_SYSTEM_CRON_INSTALL` | Optional | Skip writes to `/etc/cron.d` while keeping cron config (`true`/`false`); the managed hourly script still exits when sync is disabled |
|
|
161
|
+
| `ALPHACLAW_GIT_SHIM_PATH` | Optional | Install the managed git auth shim at this path and prepend its directory to runtime `PATH` (default `/usr/local/bin/git`) |
|
|
162
|
+
| `ALPHACLAW_GIT_ASKPASS_PATH` | Optional | Install the git askpass helper at this path (default `$TMPDIR/alphaclaw-git-askpass.sh`) |
|
|
160
163
|
| `TRUST_PROXY_HOPS` | Optional | Trust proxy hop count for correct client IP |
|
|
164
|
+
| `REMOTE_MCP_URL` | Optional | Upstream remote MCP server URL. When set together with `REMOTE_MCP_API_TOKEN`, AlphaClaw writes a managed `mcp.servers.<name>` entry to `openclaw.json` on every gateway start. |
|
|
165
|
+
| `REMOTE_MCP_API_TOKEN` | Optional | Bearer token for the remote MCP server. Persisted in `openclaw.json` as the `${REMOTE_MCP_API_TOKEN}` reference, never as plaintext. |
|
|
166
|
+
| `REMOTE_MCP_NAME` | Optional | Key under `mcp.servers.<name>`. Defaults to `remote`. Set it to label the entry (e.g. `sure`, `notion`). |
|
|
167
|
+
| `REMOTE_MCP_PROXY_URL` | Optional | When set, OpenClaw connects here instead of `REMOTE_MCP_URL`. Intended for a same-host scanning proxy (e.g. `pipelock mcp proxy --listen <REMOTE_MCP_PROXY_URL> --upstream <REMOTE_MCP_URL>`). Implementation is proxy-agnostic. |
|
|
168
|
+
|
|
169
|
+
## OpenAI-compatible `/v1` proxy
|
|
170
|
+
|
|
171
|
+
AlphaClaw can expose an OpenAI-compatible API surface on the same public port as the Setup UI. It is disabled by default. Enable it from the Setup UI under General -> Features -> API; the setting is persisted in `alphaclaw.json` in the OpenClaw repo so workspace sync can commit the change.
|
|
172
|
+
|
|
173
|
+
| Path | Method | Notes |
|
|
174
|
+
| ------------------------------- | ------- | ------------------------------------------------------------------ |
|
|
175
|
+
| `/v1/chat/completions` | POST | Streams when `stream: true`. Use `model: "openclaw/default"` or `openclaw/<agentId>`. |
|
|
176
|
+
| `/v1/responses` | POST | OpenClaw's `/v1/responses` surface (enabled together with chat completions). |
|
|
177
|
+
| `/v1/embeddings` | POST | Routes to OpenClaw's embeddings endpoint. |
|
|
178
|
+
| `/v1/models`, `/v1/models/<id>` | GET | Lists OpenClaw agent targets. |
|
|
179
|
+
|
|
180
|
+
When enabled, the proxy forwards requests to the loopback OpenClaw gateway. AlphaClaw requires `Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>` and rejects requests when the gateway token is missing or does not match before forwarding to OpenClaw. Failed bearer-token attempts are rate-limited before proxying. The setup-UI cookie is stripped before forwarding, hop-by-hop response headers are not passed through, and `/v1` JSON request bodies are accepted up to 50 MB. When disabled or missing from `alphaclaw.json`, `/v1` requests return 404.
|
|
181
|
+
|
|
182
|
+
**Security boundary (important).** OpenClaw treats `/v1/chat/completions` as a full operator-access surface. A caller with a valid `OPENCLAW_GATEWAY_TOKEN` can run any tool the configured agent profile allows. Treat this token like an owner credential:
|
|
183
|
+
|
|
184
|
+
- Use this surface only for trusted server-to-server callers (for example, a self-hosted app that needs OpenClaw as its external assistant).
|
|
185
|
+
- Do not hand the gateway token to end-user clients.
|
|
186
|
+
- If your front door is public (Render, Fly, fly-style PaaS), make sure `SETUP_PASSWORD` is strong and that the gateway token is held by exactly one trusted backend.
|
|
187
|
+
|
|
188
|
+
When `REMOTE_MCP_URL` + `REMOTE_MCP_API_TOKEN` are set, AlphaClaw also registers an `mcp.servers.<REMOTE_MCP_NAME>` block (default key `remote`) in `openclaw.json` so the agent can call back into that remote MCP server. Set `REMOTE_MCP_PROXY_URL` to route those callbacks through a same-host scanning proxy (for example a Pipelock MCP reverse proxy running in the same container).
|
|
161
189
|
|
|
162
190
|
## Security Notes
|
|
163
191
|
|
package/bin/alphaclaw.js
CHANGED
|
@@ -6,6 +6,10 @@ const os = require("os");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const { execSync } = require("child_process");
|
|
8
8
|
const {
|
|
9
|
+
shouldSkipSystemCronInstall,
|
|
10
|
+
resolveGitAskPassPath,
|
|
11
|
+
resolveGitShimPath,
|
|
12
|
+
prependGitShimDirToPath,
|
|
9
13
|
normalizeGitSyncFilePath,
|
|
10
14
|
validateGitSyncFilePath,
|
|
11
15
|
resolveRealGitPath,
|
|
@@ -16,6 +20,9 @@ const {
|
|
|
16
20
|
restoreMissingOpenclawConfigFromRemote,
|
|
17
21
|
} = require("../lib/cli/openclaw-config-restore");
|
|
18
22
|
const { buildSecretReplacements } = require("../lib/server/helpers");
|
|
23
|
+
const {
|
|
24
|
+
migrateLegacyTelegramStreamingConfig,
|
|
25
|
+
} = require("../lib/server/openclaw-config-migrations");
|
|
19
26
|
const {
|
|
20
27
|
migrateManagedInternalFiles,
|
|
21
28
|
} = require("../lib/server/internal-files-migration");
|
|
@@ -278,7 +285,7 @@ const runGitSync = () => {
|
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
const realGitPath = resolveRealGitPath({
|
|
281
|
-
shimPath:
|
|
288
|
+
shimPath: resolveGitShimPath(),
|
|
282
289
|
});
|
|
283
290
|
if (!realGitPath) {
|
|
284
291
|
console.error(
|
|
@@ -631,7 +638,11 @@ if (fs.existsSync(hourlyGitSyncPath)) {
|
|
|
631
638
|
}
|
|
632
639
|
|
|
633
640
|
const cronFilePath = "/etc/cron.d/openclaw-hourly-sync";
|
|
634
|
-
if (
|
|
641
|
+
if (shouldSkipSystemCronInstall()) {
|
|
642
|
+
console.log(
|
|
643
|
+
"[alphaclaw] System cron setup skipped by ALPHACLAW_SKIP_SYSTEM_CRON_INSTALL",
|
|
644
|
+
);
|
|
645
|
+
} else if (cronEnabled) {
|
|
635
646
|
const cronContent = [
|
|
636
647
|
"SHELL=/bin/bash",
|
|
637
648
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
@@ -732,7 +743,10 @@ if (fs.existsSync(configPath)) {
|
|
|
732
743
|
if (!cfg.plugins.load) cfg.plugins.load = {};
|
|
733
744
|
if (!Array.isArray(cfg.plugins.load.paths)) cfg.plugins.load.paths = [];
|
|
734
745
|
if (!cfg.plugins.entries) cfg.plugins.entries = {};
|
|
735
|
-
let changed =
|
|
746
|
+
let changed = migrateLegacyTelegramStreamingConfig(cfg);
|
|
747
|
+
if (changed) {
|
|
748
|
+
console.log("[alphaclaw] Migrated legacy Telegram streaming config");
|
|
749
|
+
}
|
|
736
750
|
|
|
737
751
|
if (process.env.TELEGRAM_BOT_TOKEN && !cfg.channels.telegram) {
|
|
738
752
|
cfg.channels.telegram = {
|
|
@@ -819,11 +833,17 @@ try {
|
|
|
819
833
|
|
|
820
834
|
try {
|
|
821
835
|
const gitAskPassSrc = path.join(__dirname, "..", "lib", "scripts", "git-askpass");
|
|
822
|
-
const gitAskPassDest =
|
|
836
|
+
const gitAskPassDest = resolveGitAskPassPath({
|
|
837
|
+
tmpDir: os.tmpdir(),
|
|
838
|
+
});
|
|
823
839
|
const gitShimTemplatePath = path.join(__dirname, "..", "lib", "scripts", "git");
|
|
824
|
-
const gitShimDest =
|
|
840
|
+
const gitShimDest = resolveGitShimPath();
|
|
841
|
+
process.env.PATH = prependGitShimDirToPath({
|
|
842
|
+
shimPath: gitShimDest,
|
|
843
|
+
});
|
|
825
844
|
|
|
826
845
|
if (fs.existsSync(gitAskPassSrc)) {
|
|
846
|
+
fs.mkdirSync(path.dirname(gitAskPassDest), { recursive: true });
|
|
827
847
|
fs.copyFileSync(gitAskPassSrc, gitAskPassDest);
|
|
828
848
|
fs.chmodSync(gitAskPassDest, 0o755);
|
|
829
849
|
}
|
|
@@ -837,7 +857,9 @@ try {
|
|
|
837
857
|
const gitShimTemplate = fs.readFileSync(gitShimTemplatePath, "utf8");
|
|
838
858
|
const gitShimContent = gitShimTemplate
|
|
839
859
|
.replace("@@REAL_GIT@@", realGitPath)
|
|
840
|
-
.replace("@@OPENCLAW_REPO_ROOT@@", openclawDir)
|
|
860
|
+
.replace("@@OPENCLAW_REPO_ROOT@@", openclawDir)
|
|
861
|
+
.replace("@@ASKPASS_PATH@@", gitAskPassDest);
|
|
862
|
+
fs.mkdirSync(path.dirname(gitShimDest), { recursive: true });
|
|
841
863
|
fs.writeFileSync(gitShimDest, gitShimContent, { mode: 0o755 });
|
|
842
864
|
console.log("[alphaclaw] git auth shim installed");
|
|
843
865
|
}
|
package/lib/cli/git-runtime.js
CHANGED
|
@@ -2,6 +2,43 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { execSync } = require("child_process");
|
|
4
4
|
|
|
5
|
+
const isEnabledEnvFlag = (value) =>
|
|
6
|
+
["1", "true", "yes", "on"].includes(
|
|
7
|
+
String(value || "").trim().toLowerCase(),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const shouldSkipSystemCronInstall = ({ env = process.env } = {}) =>
|
|
11
|
+
isEnabledEnvFlag(env.ALPHACLAW_SKIP_SYSTEM_CRON_INSTALL);
|
|
12
|
+
|
|
13
|
+
const resolveGitAskPassPath = ({ env = process.env, tmpDir = "/tmp" } = {}) => {
|
|
14
|
+
const explicitPath = String(env.ALPHACLAW_GIT_ASKPASS_PATH || "").trim();
|
|
15
|
+
if (explicitPath) return explicitPath;
|
|
16
|
+
|
|
17
|
+
const runtimeTmpDir =
|
|
18
|
+
String(env.TMPDIR || "").trim() || String(tmpDir || "").trim() || "/tmp";
|
|
19
|
+
return path.join(runtimeTmpDir, "alphaclaw-git-askpass.sh");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const resolveGitShimPath = ({ env = process.env } = {}) => {
|
|
23
|
+
const explicitPath = String(env.ALPHACLAW_GIT_SHIM_PATH || "").trim();
|
|
24
|
+
return explicitPath || "/usr/local/bin/git";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const prependGitShimDirToPath = ({
|
|
28
|
+
env = process.env,
|
|
29
|
+
shimPath = resolveGitShimPath({ env }),
|
|
30
|
+
} = {}) => {
|
|
31
|
+
if (!String(env.ALPHACLAW_GIT_SHIM_PATH || "").trim()) {
|
|
32
|
+
return env.PATH || process.env.PATH || "";
|
|
33
|
+
}
|
|
34
|
+
const shimDir = path.dirname(String(shimPath || "").trim());
|
|
35
|
+
if (!shimDir || shimDir === ".") return env.PATH || process.env.PATH || "";
|
|
36
|
+
const pathEntries = String(env.PATH || process.env.PATH || "")
|
|
37
|
+
.split(path.delimiter)
|
|
38
|
+
.filter((entry) => entry && entry !== shimDir);
|
|
39
|
+
return [shimDir, ...pathEntries].join(path.delimiter);
|
|
40
|
+
};
|
|
41
|
+
|
|
5
42
|
const normalizeGitSyncFilePath = (requestedFilePath) => {
|
|
6
43
|
const rawPath = String(requestedFilePath || "").trim();
|
|
7
44
|
if (!rawPath) return "";
|
|
@@ -90,6 +127,10 @@ const shouldRefreshHourlyGitSyncScript = ({
|
|
|
90
127
|
};
|
|
91
128
|
|
|
92
129
|
module.exports = {
|
|
130
|
+
shouldSkipSystemCronInstall,
|
|
131
|
+
resolveGitAskPassPath,
|
|
132
|
+
resolveGitShimPath,
|
|
133
|
+
prependGitShimDirToPath,
|
|
93
134
|
normalizeGitSyncFilePath,
|
|
94
135
|
validateGitSyncFilePath,
|
|
95
136
|
resolveRealGitPath,
|
|
@@ -27,7 +27,7 @@ const resolveCurrentBranch = ({ execSyncImpl, openclawDir }) => {
|
|
|
27
27
|
|
|
28
28
|
const createGitEnv = ({ fsModule, osModule, env, processId }) => {
|
|
29
29
|
const githubToken = String(env.GITHUB_TOKEN || "").trim();
|
|
30
|
-
const gitEnv = { ...env };
|
|
30
|
+
const gitEnv = { ...env, PATH: env.PATH || process.env.PATH };
|
|
31
31
|
if (!githubToken) {
|
|
32
32
|
return { gitEnv, askPassPath: "" };
|
|
33
33
|
}
|