@chrysb/alphaclaw 0.9.13 → 0.9.15
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 -1
- package/bin/alphaclaw.js +53 -135
- package/lib/cli/openclaw-config-restore.js +162 -0
- package/lib/public/css/explorer.css +3 -1
- package/lib/public/dist/app.bundle.js +474 -475
- package/lib/public/js/components/agents-tab/agent-identity-section.js +1 -1
- package/lib/public/js/components/general/use-general-tab.js +25 -8
- package/lib/public/js/components/modal-shell.js +0 -3
- package/lib/public/js/components/sidebar.js +2 -1
- package/lib/public/js/lib/agent-identity.js +8 -0
- package/lib/public/js/lib/api.js +7 -5
- package/lib/server/routes/pairings.js +120 -8
- package/lib/server/routes/system.js +104 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -76,13 +76,14 @@ Or with Docker:
|
|
|
76
76
|
|
|
77
77
|
```dockerfile
|
|
78
78
|
FROM node:22-slim
|
|
79
|
-
RUN apt-get update && apt-get install -y git curl procps cron && rm -rf /var/lib/apt/lists/*
|
|
79
|
+
RUN apt-get update && apt-get install -y git curl procps cron tini && rm -rf /var/lib/apt/lists/*
|
|
80
80
|
WORKDIR /app
|
|
81
81
|
COPY package.json ./
|
|
82
82
|
RUN npm install --omit=dev
|
|
83
83
|
ENV PATH="/app/node_modules/.bin:$PATH"
|
|
84
84
|
ENV ALPHACLAW_ROOT_DIR=/data
|
|
85
85
|
EXPOSE 3000
|
|
86
|
+
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
86
87
|
CMD ["alphaclaw", "start"]
|
|
87
88
|
```
|
|
88
89
|
|
package/bin/alphaclaw.js
CHANGED
|
@@ -11,6 +11,10 @@ const {
|
|
|
11
11
|
resolveRealGitPath,
|
|
12
12
|
shouldRefreshHourlyGitSyncScript,
|
|
13
13
|
} = require("../lib/cli/git-runtime");
|
|
14
|
+
const {
|
|
15
|
+
ensureMainUpstream,
|
|
16
|
+
restoreMissingOpenclawConfigFromRemote,
|
|
17
|
+
} = require("../lib/cli/openclaw-config-restore");
|
|
14
18
|
const { buildSecretReplacements } = require("../lib/server/helpers");
|
|
15
19
|
const {
|
|
16
20
|
migrateManagedInternalFiles,
|
|
@@ -666,146 +670,60 @@ try {
|
|
|
666
670
|
// ---------------------------------------------------------------------------
|
|
667
671
|
|
|
668
672
|
const configPath = path.join(openclawDir, "openclaw.json");
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
.replace(/^https:\/\/github\.com\//, "")
|
|
679
|
-
.replace(/\.git$/, "");
|
|
680
|
-
const remoteUrl = `https://github.com/${repoUrl}.git`;
|
|
681
|
-
try {
|
|
682
|
-
execSync(`git remote set-url origin "${remoteUrl}"`, {
|
|
683
|
-
cwd: openclawDir,
|
|
684
|
-
stdio: "ignore",
|
|
685
|
-
});
|
|
686
|
-
console.log("[alphaclaw] Repo ready");
|
|
687
|
-
} catch {}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Migration path: scrub persisted PATs from existing GitHub origin URLs.
|
|
673
|
+
const githubRepo = process.env.GITHUB_WORKSPACE_REPO;
|
|
674
|
+
|
|
675
|
+
if (fs.existsSync(path.join(openclawDir, ".git"))) {
|
|
676
|
+
if (githubRepo) {
|
|
677
|
+
const repoUrl = githubRepo
|
|
678
|
+
.replace(/^git@github\.com:/, "")
|
|
679
|
+
.replace(/^https:\/\/github\.com\//, "")
|
|
680
|
+
.replace(/\.git$/, "");
|
|
681
|
+
const remoteUrl = `https://github.com/${repoUrl}.git`;
|
|
691
682
|
try {
|
|
692
|
-
|
|
683
|
+
execSync(`git remote set-url origin "${remoteUrl}"`, {
|
|
693
684
|
cwd: openclawDir,
|
|
694
|
-
stdio:
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const match = existingOrigin.match(
|
|
698
|
-
/^https:\/\/[^/@]+@github\.com\/(.+)$/i,
|
|
699
|
-
);
|
|
700
|
-
if (match?.[1]) {
|
|
701
|
-
const cleanedPath = String(match[1]).replace(/\.git$/i, "");
|
|
702
|
-
const cleanedOrigin = `https://github.com/${cleanedPath}.git`;
|
|
703
|
-
execSync(`git remote set-url origin "${cleanedOrigin}"`, {
|
|
704
|
-
cwd: openclawDir,
|
|
705
|
-
stdio: "ignore",
|
|
706
|
-
});
|
|
707
|
-
console.log("[alphaclaw] Scrubbed tokenized GitHub remote URL");
|
|
708
|
-
}
|
|
685
|
+
stdio: "ignore",
|
|
686
|
+
});
|
|
687
|
+
console.log("[alphaclaw] Repo ready");
|
|
709
688
|
} catch {}
|
|
689
|
+
}
|
|
710
690
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
' *) echo "" ;;',
|
|
743
|
-
"esac",
|
|
744
|
-
"",
|
|
745
|
-
].join("\n"),
|
|
746
|
-
{ mode: 0o700 },
|
|
747
|
-
);
|
|
748
|
-
gitEnv.GITHUB_TOKEN = githubToken;
|
|
749
|
-
gitEnv.GIT_TERMINAL_PROMPT = "0";
|
|
750
|
-
gitEnv.GIT_ASKPASS = askPassPath;
|
|
751
|
-
}
|
|
752
|
-
execSync(`git ls-remote --exit-code --heads origin "${branch}"`, {
|
|
753
|
-
cwd: openclawDir,
|
|
754
|
-
stdio: "ignore",
|
|
755
|
-
env: gitEnv,
|
|
756
|
-
});
|
|
757
|
-
execSync(`git fetch --quiet origin "${branch}"`, {
|
|
758
|
-
cwd: openclawDir,
|
|
759
|
-
stdio: "ignore",
|
|
760
|
-
env: gitEnv,
|
|
761
|
-
});
|
|
762
|
-
try {
|
|
763
|
-
execSync("git show-ref --verify --quiet refs/heads/main", {
|
|
764
|
-
cwd: openclawDir,
|
|
765
|
-
stdio: "ignore",
|
|
766
|
-
});
|
|
767
|
-
try {
|
|
768
|
-
execSync("git rev-parse --abbrev-ref --symbolic-full-name main@{upstream}", {
|
|
769
|
-
cwd: openclawDir,
|
|
770
|
-
stdio: "ignore",
|
|
771
|
-
});
|
|
772
|
-
} catch {
|
|
773
|
-
execSync("git branch --set-upstream-to=origin/main main", {
|
|
774
|
-
cwd: openclawDir,
|
|
775
|
-
stdio: "ignore",
|
|
776
|
-
env: gitEnv,
|
|
777
|
-
});
|
|
778
|
-
console.log("[alphaclaw] Set main upstream to origin/main");
|
|
779
|
-
}
|
|
780
|
-
} catch {}
|
|
781
|
-
const remoteConfig = String(
|
|
782
|
-
execSync(`git show "origin/${branch}:openclaw.json"`, {
|
|
783
|
-
cwd: openclawDir,
|
|
784
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
785
|
-
encoding: "utf8",
|
|
786
|
-
env: gitEnv,
|
|
787
|
-
}),
|
|
788
|
-
);
|
|
789
|
-
if (remoteConfig.trim()) {
|
|
790
|
-
fs.writeFileSync(configPath, remoteConfig);
|
|
791
|
-
console.log(
|
|
792
|
-
`[alphaclaw] Restored openclaw.json from origin/${branch}`,
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
} catch (e) {
|
|
796
|
-
console.log(
|
|
797
|
-
`[alphaclaw] Remote config restore skipped: ${String(e.message || "").slice(0, 200)}`,
|
|
798
|
-
);
|
|
799
|
-
} finally {
|
|
800
|
-
if (githubToken) {
|
|
801
|
-
try {
|
|
802
|
-
fs.rmSync(askPassPath, { force: true });
|
|
803
|
-
} catch {}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
bootRestoreConfigFromRemote();
|
|
691
|
+
// Migration path: scrub persisted PATs from existing GitHub origin URLs.
|
|
692
|
+
try {
|
|
693
|
+
const existingOrigin = execSync("git remote get-url origin", {
|
|
694
|
+
cwd: openclawDir,
|
|
695
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
696
|
+
encoding: "utf8",
|
|
697
|
+
}).trim();
|
|
698
|
+
const match = existingOrigin.match(/^https:\/\/[^/@]+@github\.com\/(.+)$/i);
|
|
699
|
+
if (match?.[1]) {
|
|
700
|
+
const cleanedPath = String(match[1]).replace(/\.git$/i, "");
|
|
701
|
+
const cleanedOrigin = `https://github.com/${cleanedPath}.git`;
|
|
702
|
+
execSync(`git remote set-url origin "${cleanedOrigin}"`, {
|
|
703
|
+
cwd: openclawDir,
|
|
704
|
+
stdio: "ignore",
|
|
705
|
+
});
|
|
706
|
+
console.log("[alphaclaw] Scrubbed tokenized GitHub remote URL");
|
|
707
|
+
}
|
|
708
|
+
} catch {}
|
|
709
|
+
|
|
710
|
+
restoreMissingOpenclawConfigFromRemote({
|
|
711
|
+
openclawDir,
|
|
712
|
+
configPath,
|
|
713
|
+
env: process.env,
|
|
714
|
+
});
|
|
715
|
+
if (
|
|
716
|
+
ensureMainUpstream({
|
|
717
|
+
openclawDir,
|
|
718
|
+
gitEnv: process.env,
|
|
719
|
+
})
|
|
720
|
+
) {
|
|
721
|
+
console.log("[alphaclaw] Set main upstream to origin/main");
|
|
808
722
|
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (fs.existsSync(configPath)) {
|
|
726
|
+
console.log("[alphaclaw] Config exists, reconciling channels...");
|
|
809
727
|
|
|
810
728
|
try {
|
|
811
729
|
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { execSync } = require("child_process");
|
|
7
|
+
|
|
8
|
+
const kOpenclawConfigFile = "openclaw.json";
|
|
9
|
+
|
|
10
|
+
const quoteArg = (value) => `'${String(value || "").replace(/'/g, "'\"'\"'")}'`;
|
|
11
|
+
|
|
12
|
+
const resolveCurrentBranch = ({ execSyncImpl, openclawDir }) => {
|
|
13
|
+
try {
|
|
14
|
+
return (
|
|
15
|
+
String(
|
|
16
|
+
execSyncImpl("git symbolic-ref --short HEAD", {
|
|
17
|
+
cwd: openclawDir,
|
|
18
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
}),
|
|
21
|
+
).trim() || "main"
|
|
22
|
+
);
|
|
23
|
+
} catch {
|
|
24
|
+
return "main";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const createGitEnv = ({ fsModule, osModule, env, processId }) => {
|
|
29
|
+
const githubToken = String(env.GITHUB_TOKEN || "").trim();
|
|
30
|
+
const gitEnv = { ...env };
|
|
31
|
+
if (!githubToken) {
|
|
32
|
+
return { gitEnv, askPassPath: "" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const askPassPath = path.join(
|
|
36
|
+
osModule.tmpdir(),
|
|
37
|
+
`alphaclaw-boot-git-askpass-${processId}.sh`,
|
|
38
|
+
);
|
|
39
|
+
fsModule.writeFileSync(
|
|
40
|
+
askPassPath,
|
|
41
|
+
[
|
|
42
|
+
"#!/usr/bin/env sh",
|
|
43
|
+
'case "$1" in',
|
|
44
|
+
' *Username*) echo "x-access-token" ;;',
|
|
45
|
+
' *Password*) echo "${GITHUB_TOKEN:-}" ;;',
|
|
46
|
+
' *) echo "" ;;',
|
|
47
|
+
"esac",
|
|
48
|
+
"",
|
|
49
|
+
].join("\n"),
|
|
50
|
+
{ mode: 0o700 },
|
|
51
|
+
);
|
|
52
|
+
gitEnv.GITHUB_TOKEN = githubToken;
|
|
53
|
+
gitEnv.GIT_TERMINAL_PROMPT = "0";
|
|
54
|
+
gitEnv.GIT_ASKPASS = askPassPath;
|
|
55
|
+
return { gitEnv, askPassPath };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const restoreMissingOpenclawConfigFromRemote = ({
|
|
59
|
+
fsModule = fs,
|
|
60
|
+
osModule = os,
|
|
61
|
+
execSyncImpl = execSync,
|
|
62
|
+
env = process.env,
|
|
63
|
+
logger = console,
|
|
64
|
+
processId = process.pid,
|
|
65
|
+
openclawDir,
|
|
66
|
+
configPath = path.join(openclawDir || "", kOpenclawConfigFile),
|
|
67
|
+
} = {}) => {
|
|
68
|
+
if (!openclawDir) {
|
|
69
|
+
throw new Error("openclawDir is required");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (fsModule.existsSync(configPath)) {
|
|
73
|
+
logger.log(
|
|
74
|
+
"[alphaclaw] Remote config restore skipped: local openclaw.json already exists",
|
|
75
|
+
);
|
|
76
|
+
return { restored: false, skipped: true, reason: "exists" };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const branch = resolveCurrentBranch({ execSyncImpl, openclawDir });
|
|
80
|
+
const { gitEnv, askPassPath } = createGitEnv({
|
|
81
|
+
fsModule,
|
|
82
|
+
osModule,
|
|
83
|
+
env,
|
|
84
|
+
processId,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
execSyncImpl(
|
|
89
|
+
`git ls-remote --exit-code --heads origin ${quoteArg(branch)}`,
|
|
90
|
+
{
|
|
91
|
+
cwd: openclawDir,
|
|
92
|
+
stdio: "ignore",
|
|
93
|
+
env: gitEnv,
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
execSyncImpl(`git fetch --quiet origin ${quoteArg(branch)}`, {
|
|
97
|
+
cwd: openclawDir,
|
|
98
|
+
stdio: "ignore",
|
|
99
|
+
env: gitEnv,
|
|
100
|
+
});
|
|
101
|
+
const remoteConfig = String(
|
|
102
|
+
execSyncImpl(`git show ${quoteArg(`origin/${branch}:openclaw.json`)}`, {
|
|
103
|
+
cwd: openclawDir,
|
|
104
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
105
|
+
encoding: "utf8",
|
|
106
|
+
env: gitEnv,
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
if (!remoteConfig.trim()) {
|
|
110
|
+
logger.log("[alphaclaw] Remote config restore skipped: remote config empty");
|
|
111
|
+
return { restored: false, skipped: true, reason: "empty_remote", branch };
|
|
112
|
+
}
|
|
113
|
+
fsModule.writeFileSync(configPath, remoteConfig);
|
|
114
|
+
logger.log(`[alphaclaw] Restored missing openclaw.json from origin/${branch}`);
|
|
115
|
+
return { restored: true, skipped: false, reason: "missing", branch };
|
|
116
|
+
} catch (e) {
|
|
117
|
+
logger.log(
|
|
118
|
+
`[alphaclaw] Remote config restore skipped: ${String(e.message || "").slice(0, 200)}`,
|
|
119
|
+
);
|
|
120
|
+
return {
|
|
121
|
+
restored: false,
|
|
122
|
+
skipped: true,
|
|
123
|
+
reason: "error",
|
|
124
|
+
branch,
|
|
125
|
+
error: e,
|
|
126
|
+
};
|
|
127
|
+
} finally {
|
|
128
|
+
if (askPassPath) {
|
|
129
|
+
try {
|
|
130
|
+
fsModule.rmSync(askPassPath, { force: true });
|
|
131
|
+
} catch {}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const ensureMainUpstream = ({ execSyncImpl = execSync, openclawDir, gitEnv }) => {
|
|
137
|
+
try {
|
|
138
|
+
execSyncImpl("git show-ref --verify --quiet refs/heads/main", {
|
|
139
|
+
cwd: openclawDir,
|
|
140
|
+
stdio: "ignore",
|
|
141
|
+
});
|
|
142
|
+
try {
|
|
143
|
+
execSyncImpl("git rev-parse --abbrev-ref --symbolic-full-name main@{upstream}", {
|
|
144
|
+
cwd: openclawDir,
|
|
145
|
+
stdio: "ignore",
|
|
146
|
+
});
|
|
147
|
+
} catch {
|
|
148
|
+
execSyncImpl("git branch --set-upstream-to=origin/main main", {
|
|
149
|
+
cwd: openclawDir,
|
|
150
|
+
stdio: "ignore",
|
|
151
|
+
env: gitEnv,
|
|
152
|
+
});
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
} catch {}
|
|
156
|
+
return false;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
ensureMainUpstream,
|
|
161
|
+
restoreMissingOpenclawConfigFromRemote,
|
|
162
|
+
};
|