create-openclaw-bot 5.1.14 → 5.2.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/CHANGELOG.md +185 -158
- package/CHANGELOG.vi.md +279 -252
- package/README.md +12 -11
- package/README.vi.md +12 -11
- package/cli.js +416 -233
- package/package.json +1 -1
- package/setup.js +636 -318
- package/style.css +168 -37
- package/tests/smoke-cli-logic.mjs +123 -24
- package/upgrade.ps1 +90 -0
- package/upgrade.sh +93 -0
package/style.css
CHANGED
|
@@ -788,15 +788,17 @@ body::after {
|
|
|
788
788
|
/* ============ Plugin Grid ============ */
|
|
789
789
|
.plugin-grid {
|
|
790
790
|
display: grid;
|
|
791
|
-
grid-template-columns: repeat(3, 1fr);
|
|
792
|
-
gap:
|
|
791
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
792
|
+
gap: 10px;
|
|
793
|
+
align-items: stretch;
|
|
794
|
+
width: 100%;
|
|
793
795
|
}
|
|
794
796
|
|
|
795
797
|
.plugin-card {
|
|
796
798
|
display: flex;
|
|
797
|
-
align-items:
|
|
798
|
-
gap:
|
|
799
|
-
padding:
|
|
799
|
+
align-items: stretch;
|
|
800
|
+
gap: 0;
|
|
801
|
+
padding: 12px;
|
|
800
802
|
background: var(--bg-glass);
|
|
801
803
|
backdrop-filter: blur(12px);
|
|
802
804
|
-webkit-backdrop-filter: blur(12px);
|
|
@@ -805,11 +807,16 @@ body::after {
|
|
|
805
807
|
cursor: pointer;
|
|
806
808
|
transition: all var(--duration-normal) var(--ease-out);
|
|
807
809
|
position: relative;
|
|
810
|
+
min-height: 88px;
|
|
811
|
+
height: 100%;
|
|
812
|
+
min-width: 0;
|
|
813
|
+
overflow: visible;
|
|
808
814
|
}
|
|
809
815
|
|
|
810
816
|
.plugin-card:hover {
|
|
811
817
|
background: var(--bg-card-hover);
|
|
812
818
|
border-color: rgba(255, 255, 255, 0.15);
|
|
819
|
+
z-index: 5;
|
|
813
820
|
}
|
|
814
821
|
|
|
815
822
|
.plugin-card--selected {
|
|
@@ -824,59 +831,183 @@ body::after {
|
|
|
824
831
|
height: 0;
|
|
825
832
|
}
|
|
826
833
|
|
|
827
|
-
.plugin-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
834
|
+
.plugin-card__info {
|
|
835
|
+
width: 100%;
|
|
836
|
+
min-width: 0;
|
|
837
|
+
min-height: 62px;
|
|
838
|
+
display: flex;
|
|
839
|
+
flex-direction: column;
|
|
840
|
+
gap: 8px;
|
|
831
841
|
}
|
|
832
842
|
|
|
833
|
-
.plugin-
|
|
834
|
-
|
|
843
|
+
.plugin-card__topline {
|
|
844
|
+
display: flex;
|
|
845
|
+
align-items: center;
|
|
846
|
+
justify-content: space-between;
|
|
847
|
+
gap: 12px;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.plugin-card__titleline {
|
|
851
|
+
display: flex;
|
|
852
|
+
align-items: center;
|
|
853
|
+
gap: 10px;
|
|
835
854
|
min-width: 0;
|
|
855
|
+
flex: 1;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.plugin-card__icon {
|
|
859
|
+
font-size: 20px;
|
|
860
|
+
flex-shrink: 0;
|
|
861
|
+
width: 24px;
|
|
862
|
+
text-align: center;
|
|
836
863
|
}
|
|
837
864
|
|
|
838
865
|
.plugin-card__name {
|
|
839
866
|
font-size: 13px;
|
|
840
867
|
font-weight: 600;
|
|
841
868
|
color: var(--text-primary);
|
|
842
|
-
|
|
869
|
+
line-height: 1.35;
|
|
870
|
+
display: -webkit-box;
|
|
871
|
+
-webkit-line-clamp: 2;
|
|
872
|
+
-webkit-box-orient: vertical;
|
|
873
|
+
overflow: hidden;
|
|
874
|
+
margin-bottom: 0;
|
|
875
|
+
padding-right: 0;
|
|
876
|
+
min-width: 0;
|
|
843
877
|
}
|
|
844
878
|
|
|
845
|
-
.plugin-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
879
|
+
.plugin-card__subline {
|
|
880
|
+
display: flex;
|
|
881
|
+
align-items: center;
|
|
882
|
+
justify-content: flex-start;
|
|
883
|
+
padding-left: 5px;
|
|
884
|
+
gap: 10px;
|
|
885
|
+
min-height: 22px;
|
|
849
886
|
}
|
|
850
887
|
|
|
851
|
-
.plugin-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
888
|
+
.plugin-card__badge-slot {
|
|
889
|
+
width: auto;
|
|
890
|
+
min-width: 0;
|
|
891
|
+
display: flex;
|
|
892
|
+
justify-content: flex-end;
|
|
893
|
+
align-items: center;
|
|
856
894
|
}
|
|
857
895
|
|
|
858
|
-
.plugin-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
896
|
+
.plugin-card__badge {
|
|
897
|
+
display: inline-flex;
|
|
898
|
+
align-items: center;
|
|
899
|
+
gap: 4px;
|
|
900
|
+
padding: 3px 8px;
|
|
901
|
+
border-radius: 999px;
|
|
902
|
+
font-size: 9px;
|
|
903
|
+
font-weight: 700;
|
|
904
|
+
white-space: nowrap;
|
|
905
|
+
flex-shrink: 0;
|
|
906
|
+
letter-spacing: 0.01em;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.plugin-card__badge--placeholder {
|
|
910
|
+
visibility: hidden;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.plugin-card__badge--recommended {
|
|
914
|
+
background: rgba(255, 107, 53, 0.14);
|
|
915
|
+
color: #ffd3bf;
|
|
916
|
+
border: 1px solid rgba(255, 107, 53, 0.34);
|
|
917
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.plugin-card__hint-slot {
|
|
867
921
|
display: flex;
|
|
922
|
+
justify-content: flex-start;
|
|
923
|
+
align-items: center;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.plugin-card__hint {
|
|
927
|
+
position: relative;
|
|
928
|
+
display: inline-flex;
|
|
868
929
|
align-items: center;
|
|
869
930
|
justify-content: center;
|
|
870
|
-
|
|
871
|
-
|
|
931
|
+
width: 22px;
|
|
932
|
+
height: 22px;
|
|
933
|
+
border-radius: 50%;
|
|
934
|
+
background: rgba(255, 255, 255, 0.08);
|
|
935
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
936
|
+
color: rgba(255, 255, 255, 0.88);
|
|
937
|
+
font-size: 10px;
|
|
938
|
+
font-weight: 700;
|
|
872
939
|
transition: all var(--duration-fast) var(--ease-out);
|
|
940
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
873
941
|
}
|
|
874
942
|
|
|
875
|
-
.plugin-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
943
|
+
.plugin-card__hint--placeholder {
|
|
944
|
+
visibility: hidden;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.plugin-card__hint:hover,
|
|
948
|
+
.plugin-card__hint:focus-visible {
|
|
949
|
+
background: rgba(255, 107, 53, 0.18);
|
|
950
|
+
border-color: rgba(255, 107, 53, 0.38);
|
|
951
|
+
color: #fff4ee;
|
|
952
|
+
box-shadow: 0 10px 24px rgba(255, 107, 53, 0.18);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
.plugin-card__tooltip {
|
|
956
|
+
position: absolute;
|
|
957
|
+
left: 0;
|
|
958
|
+
top: calc(100% + 10px);
|
|
959
|
+
width: 240px;
|
|
960
|
+
padding: 11px 13px;
|
|
961
|
+
border-radius: 14px;
|
|
962
|
+
background: linear-gradient(180deg, rgba(30, 22, 22, 0.98), rgba(20, 20, 30, 0.98));
|
|
963
|
+
border: 1px solid rgba(255, 145, 96, 0.22);
|
|
964
|
+
box-shadow: 0 22px 48px rgba(0, 0, 0, 0.42);
|
|
965
|
+
color: #f3dde0;
|
|
966
|
+
font-size: 11px;
|
|
967
|
+
line-height: 1.5;
|
|
968
|
+
opacity: 0;
|
|
969
|
+
visibility: hidden;
|
|
970
|
+
transform: translateY(6px);
|
|
971
|
+
transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out), visibility var(--duration-fast) var(--ease-out);
|
|
972
|
+
pointer-events: none;
|
|
973
|
+
z-index: 20;
|
|
974
|
+
white-space: normal;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.plugin-card__tooltip::before {
|
|
978
|
+
content: '';
|
|
979
|
+
position: absolute;
|
|
980
|
+
top: -6px;
|
|
981
|
+
left: 8px;
|
|
982
|
+
width: 12px;
|
|
983
|
+
height: 12px;
|
|
984
|
+
background: rgba(27, 21, 28, 0.98);
|
|
985
|
+
border-left: 1px solid rgba(255, 145, 96, 0.22);
|
|
986
|
+
border-top: 1px solid rgba(255, 145, 96, 0.22);
|
|
987
|
+
transform: rotate(45deg);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.plugin-card__hint:hover .plugin-card__tooltip,
|
|
991
|
+
.plugin-card__hint:focus-visible .plugin-card__tooltip {
|
|
992
|
+
opacity: 1;
|
|
993
|
+
visibility: visible;
|
|
994
|
+
transform: translateY(0);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.plugin-card__hint:hover,
|
|
998
|
+
.plugin-card__hint:focus-visible {
|
|
999
|
+
z-index: 8;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.plugin-card__switch {
|
|
1003
|
+
pointer-events: none;
|
|
1004
|
+
flex-shrink: 0;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.plugin-card--selected .plugin-card__badge--recommended {
|
|
1008
|
+
background: rgba(255, 107, 53, 0.2);
|
|
1009
|
+
border-color: rgba(255, 107, 53, 0.38);
|
|
1010
|
+
color: #ffd1bc;
|
|
880
1011
|
}
|
|
881
1012
|
|
|
882
1013
|
/* ============ Prompt Notice ============ */
|
|
@@ -86,6 +86,16 @@ checks.push(() => expectMatch(
|
|
|
86
86
|
'Native 9Router flow must auto-install 9Router'
|
|
87
87
|
));
|
|
88
88
|
|
|
89
|
+
checks.push(() => expect(
|
|
90
|
+
cli.includes('function providerSupportsMemoryEmbeddings(providerKey) {')
|
|
91
|
+
&& cli.includes("supportsEmbeddings: true")
|
|
92
|
+
&& cli.includes("supportsEmbeddings: false")
|
|
93
|
+
&& cli.includes("function getCliSkillChoices({ providerKey, isVi }) {")
|
|
94
|
+
&& cli.includes("skill.value !== 'memory'")
|
|
95
|
+
&& cli.includes("providerSupportsMemoryEmbeddings(providerKey)"),
|
|
96
|
+
'CLI skill labels must compute memory recommendations from provider embedding capability instead of hardcoding them'
|
|
97
|
+
));
|
|
98
|
+
|
|
89
99
|
checks.push(() => expectMatch(
|
|
90
100
|
cli,
|
|
91
101
|
/if \(providerKey === '9router'\) \{[\s\S]*shouldReuseInstalledGlobals\(\) && is9RouterInstalled\(\)[\s\S]*Reusing the installed 9Router[\s\S]*else if \(!is9RouterInstalled\(\)\)/s,
|
|
@@ -97,14 +107,14 @@ checks.push(() => expect(
|
|
|
97
107
|
&& cli.includes('const safeDbPath = JSON.stringify(dbPath);')
|
|
98
108
|
&& cli.includes("const ROUTER='http://localhost:20128';")
|
|
99
109
|
&& cli.includes("fetch(ROUTER + '/api/providers')")
|
|
100
|
-
&& cli.includes("build9RouterSmartRouteSyncScript(path.join(
|
|
101
|
-
'Native 9Router flow must write a smart-route sync script
|
|
110
|
+
&& cli.includes("build9RouterSmartRouteSyncScript(path.join(getProject9RouterDataDir(projectDir), 'db.json'))"),
|
|
111
|
+
'Native 9Router flow must write a smart-route sync script into the project-controlled 9Router data directory'
|
|
102
112
|
));
|
|
103
113
|
|
|
104
114
|
checks.push(() => expectMatch(
|
|
105
115
|
cli,
|
|
106
|
-
/function
|
|
107
|
-
'CLI
|
|
116
|
+
/function getProjectRuntimeEnv\(projectDir, extraEnv = \{\}\) \{[\s\S]*OPENCLAW_HOME: getProjectOpenClawHome\(projectDir\)[\s\S]*OPENCLAW_STATE_DIR: getProjectOpenClawHome\(projectDir\)[\s\S]*DATA_DIR: getProject9RouterDataDir\(projectDir\)/s,
|
|
117
|
+
'CLI native runtime must derive OPENCLAW_HOME, OPENCLAW_STATE_DIR, and DATA_DIR from the chosen project directory'
|
|
108
118
|
));
|
|
109
119
|
|
|
110
120
|
checks.push(() => expect(
|
|
@@ -146,8 +156,8 @@ checks.push(() => expectMatch(
|
|
|
146
156
|
|
|
147
157
|
checks.push(() => expectMatch(
|
|
148
158
|
cli,
|
|
149
|
-
/
|
|
150
|
-
'Native single-bot VPS must start gateway through PM2'
|
|
159
|
+
/execFileSync\('pm2', \[[\s\S]*'openclaw'[\s\S]*'gateway'[\s\S]*'run'[\s\S]*getProjectRuntimeEnv\(projectDir\)/s,
|
|
160
|
+
'Native single-bot VPS must start gateway through PM2 with the project runtime environment'
|
|
151
161
|
));
|
|
152
162
|
|
|
153
163
|
checks.push(() => expectMatch(
|
|
@@ -224,15 +234,18 @@ checks.push(() => expectMatch(
|
|
|
224
234
|
|
|
225
235
|
checks.push(() => expectMatch(
|
|
226
236
|
cli,
|
|
227
|
-
/RUN npm install -g \$\{OPENCLAW_NPM_SPEC\}
|
|
228
|
-
'Docker CLI image must install
|
|
237
|
+
/RUN npm install -g \$\{OPENCLAW_NPM_SPEC\} \$\{OPENCLAW_RUNTIME_PACKAGES\}/,
|
|
238
|
+
'Docker CLI image must install the full OpenClaw runtime package set alongside openclaw'
|
|
229
239
|
));
|
|
230
240
|
|
|
231
241
|
checks.push(() => expect(
|
|
232
242
|
cli.includes("a.add('http://' + entry.address + ':18791')")
|
|
233
243
|
&& cli.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
244
|
+
&& cli.includes("bind:'loopback'")
|
|
245
|
+
&& cli.includes("delete c.gateway.customBindHost;")
|
|
246
|
+
&& cli.includes("const gatewayBridge = 'socat TCP-LISTEN:18791,fork,reuseaddr TCP:127.0.0.1:18791 & ';")
|
|
234
247
|
&& !cli.includes("a.add(`http://${entry.address}:18791`)"),
|
|
235
|
-
'Docker CLI patch script must avoid shell-expanding ${entry.address}
|
|
248
|
+
'Docker CLI patch script must keep the gateway loopback-local behind a socat bridge and avoid shell-expanding ${entry.address}'
|
|
236
249
|
));
|
|
237
250
|
|
|
238
251
|
checks.push(() => expectMatch(
|
|
@@ -255,13 +268,13 @@ checks.push(() => expectMatch(
|
|
|
255
268
|
|
|
256
269
|
checks.push(() => expectMatch(
|
|
257
270
|
cli,
|
|
258
|
-
/function resolveNative9RouterDesktopLaunch\(\) \{[\s\S]*command: process\.execPath[\s\S]*
|
|
271
|
+
/function resolveNative9RouterDesktopLaunch\(\) \{[\s\S]*get9RouterServerEntryCandidates\(\)\.find\([\s\S]*command: process\.execPath[\s\S]*args: \[serverEntry\][\s\S]*PORT: '20128'[\s\S]*HOSTNAME: '0\.0\.0\.0'/s,
|
|
259
272
|
'Native desktop 9Router launch must bypass the interactive CLI menu by running the 9Router server entry directly'
|
|
260
273
|
));
|
|
261
274
|
|
|
262
275
|
checks.push(() => expectMatch(
|
|
263
276
|
cli,
|
|
264
|
-
/const native9RouterLaunch = resolveNative9RouterDesktopLaunch\(\);[\s\S]*spawnBackgroundProcess\(native9RouterLaunch\.command, native9RouterLaunch\.args, \{[\s\S]*
|
|
277
|
+
/const native9RouterLaunch = resolveNative9RouterDesktopLaunch\(\);[\s\S]*spawnBackgroundProcess\(native9RouterLaunch\.command, native9RouterLaunch\.args, \{[\s\S]*getProjectRuntimeEnv\(projectDir, native9RouterLaunch\.env\)/s,
|
|
265
278
|
'Native desktop 9Router flow must launch through the background helper with the resolved launch spec'
|
|
266
279
|
));
|
|
267
280
|
|
|
@@ -291,9 +304,16 @@ checks.push(() => expectMatch(
|
|
|
291
304
|
|
|
292
305
|
checks.push(() => expectOrder(
|
|
293
306
|
cli,
|
|
294
|
-
"await
|
|
307
|
+
"await ensureProjectRuntimeDirs(projectDir, isVi);",
|
|
295
308
|
"installRelayPluginForProject(projectDir, isVi);",
|
|
296
|
-
'Relay plugin install must happen after
|
|
309
|
+
'Relay plugin install must happen after preparing the project runtime directories in native flow'
|
|
310
|
+
));
|
|
311
|
+
|
|
312
|
+
checks.push(() => expect(
|
|
313
|
+
!cli.includes('Config synced to ~/.openclaw/')
|
|
314
|
+
&& !cli.includes('Config đã được sync vào ~/.openclaw/')
|
|
315
|
+
&& !cli.includes("cp -rn ${localClawDir}/. ${globalClawDir}/"),
|
|
316
|
+
'CLI native flow must no longer sync project config into the global ~/.openclaw directory'
|
|
297
317
|
));
|
|
298
318
|
|
|
299
319
|
checks.push(() => expectMatch(
|
|
@@ -334,27 +354,96 @@ checks.push(() => expectMatch(
|
|
|
334
354
|
|
|
335
355
|
checks.push(() => expectMatch(
|
|
336
356
|
setup,
|
|
337
|
-
/RUN npm install -g openclaw@2026\.4\.5
|
|
338
|
-
'Wizard Dockerfile generation must install
|
|
357
|
+
/RUN npm install -g openclaw@2026\.4\.5 \$\{openClawRuntimePackages\}/,
|
|
358
|
+
'Wizard Dockerfile generation must install the full OpenClaw runtime package set alongside openclaw'
|
|
339
359
|
));
|
|
340
360
|
|
|
341
361
|
checks.push(() => expect(
|
|
342
362
|
setup.includes("a.add('http://' + entry.address + ':18791')")
|
|
343
363
|
&& setup.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
364
|
+
&& setup.includes("bind:'loopback'")
|
|
365
|
+
&& setup.includes("delete c.gateway.customBindHost;")
|
|
366
|
+
&& setup.includes("const gatewayBridgePrefix = 'socat TCP-LISTEN:18791,fork,reuseaddr TCP:127.0.0.1:18791 & ';")
|
|
344
367
|
&& !setup.includes("a.add(\\`http://\\${entry.address}:18791\\`)"),
|
|
345
|
-
'Wizard Docker patch command must avoid shell-expanding ${entry.address}
|
|
368
|
+
'Wizard Docker patch command must keep the gateway loopback-local behind a socat bridge and avoid shell-expanding ${entry.address}'
|
|
346
369
|
));
|
|
347
370
|
|
|
348
371
|
checks.push(() => expectMatch(
|
|
349
372
|
setup,
|
|
350
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*scriptName = 'setup-openclaw-vps\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5
|
|
351
|
-
'VPS native script generation must install openclaw+pm2 and persist PM2 startup'
|
|
373
|
+
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*scriptName = 'setup-openclaw-vps\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*PROJECT_DIR="[\s\S]*export OPENCLAW_HOME="\$PROJECT_DIR\/\.openclaw"[\s\S]*export OPENCLAW_STATE_DIR="\$PROJECT_DIR\/\.openclaw"[\s\S]*export DATA_DIR="\$PROJECT_DIR\/\.9router"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*pm2@latest[\s\S]*pm2 save && pm2 startup/s,
|
|
374
|
+
'VPS native script generation must keep runtime files project-local, install openclaw+pm2, and persist PM2 startup'
|
|
375
|
+
));
|
|
376
|
+
|
|
377
|
+
checks.push(() => expect(
|
|
378
|
+
setup.includes('scriptName = isDocker ? \'setup-openclaw-docker-macos.sh\' : \'setup-openclaw-macos.sh\';')
|
|
379
|
+
&& setup.includes('scriptName = \'setup-openclaw-linux.sh\';')
|
|
380
|
+
&& setup.includes('cd "$PROJECT_DIR"')
|
|
381
|
+
&& setup.includes('export OPENCLAW_HOME="$PROJECT_DIR/.openclaw"')
|
|
382
|
+
&& setup.includes('export OPENCLAW_STATE_DIR="$PROJECT_DIR/.openclaw"')
|
|
383
|
+
&& setup.includes('export DATA_DIR="$PROJECT_DIR/.9router"'),
|
|
384
|
+
'Unix native script generation must use project-local runtime directories and launch from PROJECT_DIR'
|
|
385
|
+
));
|
|
386
|
+
|
|
387
|
+
checks.push(() => expect(
|
|
388
|
+
setup.includes("arr.push('call npm install -g 9router || goto :fail');")
|
|
389
|
+
&& setup.includes('function native9RouterServerEntryLookup() {')
|
|
390
|
+
&& setup.includes('return "node -e ')
|
|
391
|
+
&& !setup.includes('return "node -p ')
|
|
392
|
+
&& setup.includes('function windowsHiddenNodeLaunch(targetPath, extraEnv = {}) {')
|
|
393
|
+
&& setup.includes('NINE_ROUTER_ENTRY=%%I')
|
|
394
|
+
&& setup.includes("windowsHiddenNodeLaunch('%NINE_ROUTER_ENTRY%', { PORT: '20128', HOSTNAME: '0.0.0.0', DATA_DIR: '%DATA_DIR%' })")
|
|
395
|
+
&& setup.includes('NINE_ROUTER_ENTRY="$(${native9RouterServerEntryLookup()})"')
|
|
396
|
+
&& setup.includes("const p=path.join(process.env.DATA_DIR||'.9router','db.json');")
|
|
397
|
+
&& setup.includes('nohup env PORT=20128 HOSTNAME=0.0.0.0 DATA_DIR="$PWD/.9router" node "$NINE_ROUTER_ENTRY" >/tmp/9router.log 2>&1 &')
|
|
398
|
+
&& setup.includes('nohup env DATA_DIR="$PWD/.9router" node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &')
|
|
399
|
+
&& setup.includes('set "PROJECT_DIR=')
|
|
400
|
+
&& setup.includes('set "OPENCLAW_HOME=%PROJECT_DIR%\\\\.openclaw"')
|
|
401
|
+
&& setup.includes('set "OPENCLAW_STATE_DIR=%PROJECT_DIR%\\\\.openclaw"')
|
|
402
|
+
&& setup.includes('set "DATA_DIR=%PROJECT_DIR%\\\\.9router"'),
|
|
403
|
+
'Native script generation must install and start a standalone 9Router dashboard on port 20128'
|
|
404
|
+
));
|
|
405
|
+
|
|
406
|
+
checks.push(() => expect(
|
|
407
|
+
setup.includes("echo OpenClaw Dashboard: http://127.0.0.1:18791")
|
|
408
|
+
&& setup.includes("echo Other reachable URLs: http://localhost:18791")
|
|
409
|
+
&& setup.includes("echo If the dashboard asks for a Gateway Token, run: openclaw dashboard")
|
|
410
|
+
&& setup.includes("echo 9Router Dashboard: http://127.0.0.1:20128/dashboard")
|
|
411
|
+
&& !setup.includes('set "HOME=%PROJECT_DIR%"')
|
|
412
|
+
&& !setup.includes('set "USERPROFILE=%PROJECT_DIR%"'),
|
|
413
|
+
'Windows native script generation must print OpenClaw and 9Router dashboard URLs'
|
|
414
|
+
));
|
|
415
|
+
|
|
416
|
+
checks.push(() => expect(
|
|
417
|
+
setup.includes("bind: 'loopback'")
|
|
418
|
+
&& !setup.includes("bind: 'custom'")
|
|
419
|
+
&& !setup.includes("customBindHost: '0.0.0.0'")
|
|
420
|
+
&& setup.includes("state.bots[state.activeBotIndex].provider = key;")
|
|
421
|
+
&& setup.includes("state.bots[state.activeBotIndex].model = p.models[0].id;")
|
|
422
|
+
&& setup.includes("state.bots[state.activeBotIndex].provider = state.config.provider;")
|
|
423
|
+
&& setup.includes("state.bots[state.activeBotIndex].model = state.config.model;")
|
|
424
|
+
&& setup.includes("if (state.botCount <= 1 && state.bots[state.activeBotIndex]) {")
|
|
425
|
+
&& setup.includes("state.bots[state.activeBotIndex].token = botTokenEl.value;")
|
|
426
|
+
&& setup.includes("state.bots[state.activeBotIndex].apiKey = apiKeyEl.value;")
|
|
427
|
+
&& setup.includes("const authProviderName = provider.isProxy ? '9router' : state.config.provider;")
|
|
428
|
+
&& setup.includes("const authProviderName = botProvider.isProxy ? '9router' : (bot.provider || state.config.provider);")
|
|
429
|
+
&& setup.includes("const nativeSkillConfigs = state.config.skills")
|
|
430
|
+
&& setup.includes("const nativeSkillInstallCmds = nativeSkillConfigs.map((skill) => `call openclaw skills install ${skill.slug} || echo Warning: Failed to install skill ${skill.slug}`);")
|
|
431
|
+
&& setup.includes("lines.push('call npm install -g agent-browser playwright || goto :fail');")
|
|
432
|
+
&& setup.includes("lines.push('call npx playwright install chromium || goto :fail');")
|
|
433
|
+
&& setup.includes("lines.push('echo Cai skills...');")
|
|
434
|
+
&& setup.includes("const openClawRuntimePackages = 'grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api';")
|
|
435
|
+
&& setup.includes("memory: 'none'")
|
|
436
|
+
&& setup.includes("workspace: 'workspace'")
|
|
437
|
+
&& setup.includes("workspace: meta.workspaceDir")
|
|
438
|
+
&& !setup.includes("const authProviderName = provider.isProxy ? '9router' : provider.id;")
|
|
439
|
+
&& !setup.includes("const authProviderName = botProvider.isProxy ? '9router' : botProvider.id;"),
|
|
440
|
+
'Wizard native config generation must keep gateway loopback-local, preserve concrete auth provider ids, disable memory search by default, and sync single-bot provider/model selections into bot state'
|
|
352
441
|
));
|
|
353
442
|
|
|
354
443
|
checks.push(() => expectMatch(
|
|
355
444
|
setup,
|
|
356
|
-
/function
|
|
357
|
-
'Native script
|
|
445
|
+
/window\.downloadNativeScript = function\(\) \{[\s\S]*generateOutput\(\);[\s\S]*const script = window\._nativeScript;/,
|
|
446
|
+
'Native script download must regenerate the latest wizard output before reading the cached script'
|
|
358
447
|
));
|
|
359
448
|
|
|
360
449
|
checks.push(() => expectMatch(
|
|
@@ -365,8 +454,8 @@ checks.push(() => expectMatch(
|
|
|
365
454
|
|
|
366
455
|
checks.push(() => expectMatch(
|
|
367
456
|
setup,
|
|
368
|
-
/function native9RouterSyncScriptContent\(\) \{[\s\S]*path\.join\(process\.env\.
|
|
369
|
-
'Native script generation must
|
|
457
|
+
/function native9RouterSyncScriptContent\(\) \{[\s\S]*const p=path\.join\(process\.env\.DATA_DIR\|\|'\.9router','db\.json'\);[\s\S]*const ROUTER='http:\/\/localhost:20128';[\s\S]*fetch\(ROUTER\+'\/api\/providers'\)[\s\S]*d\.connections[\s\S]*smart-route/s,
|
|
458
|
+
'Native script generation must keep the 9Router sync script project-local via DATA_DIR and sync active providers from the 9Router API'
|
|
370
459
|
));
|
|
371
460
|
|
|
372
461
|
checks.push(() => expect(
|
|
@@ -377,6 +466,16 @@ checks.push(() => expect(
|
|
|
377
466
|
'9Router sync logic in setup.js must remove stale smart-route combos when providers are disabled'
|
|
378
467
|
));
|
|
379
468
|
|
|
469
|
+
checks.push(() => expect(
|
|
470
|
+
setup.includes('function providerSupportsMemoryEmbeddings(providerKey) {')
|
|
471
|
+
&& setup.includes('function getSkillDisplayName(skill, providerKey, lang) {')
|
|
472
|
+
&& setup.includes('function getSkillExtraNote(skill, providerKey, lang) {')
|
|
473
|
+
&& setup.includes('renderPluginGrid();')
|
|
474
|
+
&& setup.includes("supportsEmbeddings: true")
|
|
475
|
+
&& setup.includes("supportsEmbeddings: false"),
|
|
476
|
+
'Wizard skill cards must recompute memory recommendation labels from provider embedding capability when the provider changes'
|
|
477
|
+
));
|
|
478
|
+
|
|
380
479
|
checks.push(() => expectMatch(
|
|
381
480
|
setup,
|
|
382
481
|
/\.openclaw\/9router-smart-route-sync\.js[\s\S]*pm2 start --name openclaw-9router-sync/s,
|
|
@@ -407,13 +506,13 @@ checks.push(() => expect(
|
|
|
407
506
|
|
|
408
507
|
checks.push(() => expectMatch(
|
|
409
508
|
setup,
|
|
410
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "
|
|
509
|
+
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*NINE_ROUTER_ENTRY="\$\([\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "\$NINE_ROUTER_ENTRY" --name openclaw-multibot-9router --interpreter "\$\(command -v node\)"[\s\S]*pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw-multibot/s,
|
|
411
510
|
'VPS multi-bot native script must start the shared gateway via PM2'
|
|
412
511
|
));
|
|
413
512
|
|
|
414
513
|
checks.push(() => expectMatch(
|
|
415
514
|
setup,
|
|
416
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "
|
|
515
|
+
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*NINE_ROUTER_ENTRY="\$\([\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "\$NINE_ROUTER_ENTRY" --name openclaw-9router --interpreter "\$\(command -v node\)"[\s\S]*pm2 start --name openclaw -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw/s,
|
|
417
516
|
'VPS single-bot native script must start one bot via PM2'
|
|
418
517
|
));
|
|
419
518
|
|
package/upgrade.ps1
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# OpenClaw Upgrade Script — Windows (PowerShell)
|
|
2
|
+
# Cach dung:
|
|
3
|
+
# Nhan dup upgrade.ps1 hoac: .\upgrade.ps1
|
|
4
|
+
# irm https://raw.githubusercontent.com/tuanminhhole/openclaw-setup/main/upgrade.ps1 | iex
|
|
5
|
+
# Chi danh cho Windows. Linux/macOS/Ubuntu: dung upgrade.sh
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
|
|
9
|
+
# ── Version ──────────────────────────────────────────────────────────────────
|
|
10
|
+
$VER_STR = ""
|
|
11
|
+
try {
|
|
12
|
+
if (Test-Path "package.json") {
|
|
13
|
+
$pkg = Get-Content "package.json" -Raw | ConvertFrom-Json
|
|
14
|
+
if ($pkg.version) { $VER_STR = " v$($pkg.version)" }
|
|
15
|
+
}
|
|
16
|
+
} catch {}
|
|
17
|
+
|
|
18
|
+
# ── Banner: LOGO + BOX ───────────────────────────────────────────────────────
|
|
19
|
+
Write-Host ""
|
|
20
|
+
$logo = @(
|
|
21
|
+
'████████╗██╗ ██╗ █████╗ ███╗ ██╗███╗ ███╗██╗███╗ ██╗██╗ ██╗██╗ ██╗ ██████╗ ██╗ ███████╗',
|
|
22
|
+
'╚══██╔══╝██║ ██║██╔══██╗████╗ ██║████╗ ████║██║████╗ ██║██║ ██║██║ ██║██╔═══██╗██║ ██╔════╝',
|
|
23
|
+
' ██║ ██║ ██║███████║██╔██╗ ██║██╔████╔██║██║██╔██╗ ██║███████║███████║██║ ██║██║ █████╗ ',
|
|
24
|
+
' ██║ ██║ ██║██╔══██║██║╚██╗██║██║╚██╔╝██║██║██║╚██╗██║██╔══██║██╔══██║██║ ██║██║ ██╔══╝ ',
|
|
25
|
+
' ██║ ╚██████╔╝██║ ██║██║ ╚████║██║ ╚═╝ ██║██║██║ ╚████║██║ ██║██║ ██║╚██████╔╝███████╗███████╗',
|
|
26
|
+
' ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝'
|
|
27
|
+
)
|
|
28
|
+
foreach ($l in $logo) { Write-Host $l -ForegroundColor Red }
|
|
29
|
+
Write-Host ""
|
|
30
|
+
|
|
31
|
+
# Box — node render (handles emoji visual width correctly on all terminals)
|
|
32
|
+
$env:L1 = " 🦞 OpenClaw Setup${VER_STR} | Upgrade Script"
|
|
33
|
+
$env:L2 = " Windows (PowerShell)"
|
|
34
|
+
node -e @"
|
|
35
|
+
const RED='`u{1b}[0;31m',NC='`u{1b}[0m';
|
|
36
|
+
function vw(s){let w=0;for(const c of[...s]){const cp=c.codePointAt(0);w+=(cp>=0x1F000&&cp<=0x1FFFF?2:1);}return w;}
|
|
37
|
+
const L1=process.env.L1,L2=process.env.L2;
|
|
38
|
+
const INNER=Math.max(vw(L1),vw(L2))+2;
|
|
39
|
+
const D='─'.repeat(INNER);const pad=s=>' '.repeat(Math.max(0,INNER-vw(s)));
|
|
40
|
+
console.log(RED+'╭'+D+'╮'+NC);
|
|
41
|
+
console.log(RED+'│'+NC+L1+pad(L1)+RED+'│'+NC);
|
|
42
|
+
console.log(RED+'│'+NC+L2+pad(L2)+RED+'│'+NC);
|
|
43
|
+
console.log(RED+'╰'+D+'╯'+NC);
|
|
44
|
+
"@
|
|
45
|
+
Write-Host ""
|
|
46
|
+
|
|
47
|
+
# ── 1. Kiem tra Node.js ──────────────────────────────────────────────────────
|
|
48
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
|
49
|
+
Write-Host " ❌ Khong tim thay Node.js." -ForegroundColor Red
|
|
50
|
+
Write-Host " Tai LTS: https://nodejs.org/" -ForegroundColor Yellow
|
|
51
|
+
Read-Host "Nhan Enter de dong"; exit 1
|
|
52
|
+
}
|
|
53
|
+
$nodeVer = node -e "process.stdout.write(process.version)"
|
|
54
|
+
Write-Host " ✅ Node.js $nodeVer" -ForegroundColor Green
|
|
55
|
+
|
|
56
|
+
# ── 2. Xac dinh thu muc project ──────────────────────────────────────────────
|
|
57
|
+
$ScriptDir = $PSScriptRoot
|
|
58
|
+
if (-not $ScriptDir -or $ScriptDir -eq "") {
|
|
59
|
+
$ProjectDir = (Get-Location).Path
|
|
60
|
+
} elseif ((Test-Path (Join-Path $ScriptDir ".openclaw")) -or (Test-Path (Join-Path $ScriptDir "docker"))) {
|
|
61
|
+
$ProjectDir = $ScriptDir
|
|
62
|
+
} else {
|
|
63
|
+
$ProjectDir = (Get-Location).Path
|
|
64
|
+
}
|
|
65
|
+
Write-Host " 📁 Project: $ProjectDir" -ForegroundColor DarkGray
|
|
66
|
+
Write-Host ""
|
|
67
|
+
Set-Location $ProjectDir
|
|
68
|
+
|
|
69
|
+
# ── 3. Chay upgrade ──────────────────────────────────────────────────────────
|
|
70
|
+
Write-Host " 🔄 Dang lay CLI moi nhat va chay upgrade..." -ForegroundColor Cyan
|
|
71
|
+
Write-Host " npx luon tai create-openclaw-bot@latest — khong can cap nhat tay" -ForegroundColor DarkGray
|
|
72
|
+
Write-Host ""
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
& npx create-openclaw-bot@latest upgrade
|
|
76
|
+
$exitCode = $LASTEXITCODE
|
|
77
|
+
} catch {
|
|
78
|
+
Write-Host " ❌ Loi: $_" -ForegroundColor Red
|
|
79
|
+
Read-Host "Nhan Enter de dong"; exit 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Write-Host ""
|
|
83
|
+
if ($exitCode -eq 0) {
|
|
84
|
+
Write-Host " 🎉 Upgrade hoan tat!" -ForegroundColor Green
|
|
85
|
+
Write-Host " Dashboard: http://localhost:18791" -ForegroundColor Cyan
|
|
86
|
+
} else {
|
|
87
|
+
Write-Host " ⚠️ Ma loi: $exitCode — xem log o tren." -ForegroundColor Yellow
|
|
88
|
+
}
|
|
89
|
+
Write-Host ""
|
|
90
|
+
Read-Host "Nhan Enter de dong"
|