@xfxstudio/claworld 0.2.8 → 0.2.10-beta.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/README.md +1 -1
- package/openclaw.plugin.json +7 -63
- package/package.json +6 -2
- package/skills/claworld-help/SKILL.md +7 -3
- package/skills/claworld-join-and-chat/SKILL.md +38 -9
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +19 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +7 -1
- package/src/openclaw/installer/cli.js +46 -1
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +234 -3
- package/src/openclaw/installer/doctor.js +2 -2
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +302 -266
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +284 -79
- package/src/openclaw/plugin/onboarding.js +22 -42
- package/src/openclaw/plugin/register.js +144 -25
- package/src/openclaw/plugin/relay-client.js +237 -18
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/feedback-helper.js +1 -2
- package/src/openclaw/runtime/product-shell-helper.js +43 -9
- package/src/openclaw/runtime/tool-contracts.js +65 -3
- package/src/openclaw/runtime/tool-inventory.js +8 -1
- package/src/openclaw/runtime/world-moderation-helper.js +3 -19
- package/src/product-shell/contracts/candidate-feed.js +7 -0
- package/src/product-shell/contracts/world-manifest.js +0 -1
- package/src/product-shell/contracts/world-orchestration.js +10 -1
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
- package/src/product-shell/feedback/feedback-routes.js +0 -1
- package/src/product-shell/feedback/feedback-service.js +4 -9
- package/src/product-shell/index.js +40 -7
- package/src/product-shell/matching/matchmaking-service.js +22 -1
- package/src/product-shell/membership/membership-service.js +5 -1
- package/src/product-shell/onboarding/onboarding-service.js +10 -21
- package/src/product-shell/profile/public-identity-routes.js +60 -0
- package/src/product-shell/profile/public-identity-service.js +190 -0
- package/src/product-shell/search/search-service.js +9 -2
- package/src/product-shell/social/chat-request-routes.js +4 -1
- package/src/product-shell/social/chat-request-service.js +184 -22
- package/src/product-shell/social/friend-routes.js +1 -1
- package/src/product-shell/social/friend-service.js +16 -19
- package/src/product-shell/social/social-routes.js +2 -2
- package/src/product-shell/social/social-service.js +31 -35
- package/src/product-shell/worlds/world-admin-service.js +31 -10
- package/src/product-shell/worlds/world-broadcast-service.js +2 -2
- package/src/lib/agent-address.js +0 -46
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
export const PUBLIC_IDENTITY_STATUS = Object.freeze({
|
|
4
|
+
PENDING: 'pending',
|
|
5
|
+
READY: 'ready',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH = 40;
|
|
9
|
+
export const PUBLIC_IDENTITY_CODE_LENGTH = 6;
|
|
10
|
+
const PUBLIC_IDENTITY_CODE_ALPHABET = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
|
11
|
+
|
|
12
|
+
function normalizeText(value, fallback = null) {
|
|
13
|
+
if (value == null) return fallback;
|
|
14
|
+
const normalized = String(value).trim();
|
|
15
|
+
return normalized || fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeIsoTimestamp(value, fallback = null) {
|
|
19
|
+
const normalized = normalizeText(value, null);
|
|
20
|
+
if (!normalized) return fallback;
|
|
21
|
+
const timestamp = Date.parse(normalized);
|
|
22
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : fallback;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cloneObject(value, fallback = {}) {
|
|
26
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return { ...fallback };
|
|
27
|
+
return { ...value };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizePublicDisplayName(value, { fallback = null } = {}) {
|
|
31
|
+
const normalized = normalizeText(value, fallback);
|
|
32
|
+
if (!normalized) return fallback;
|
|
33
|
+
return normalized.slice(0, PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validatePublicDisplayName(value) {
|
|
37
|
+
const normalized = normalizeText(value, null);
|
|
38
|
+
if (!normalized) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
code: 'display_name_required',
|
|
42
|
+
message: 'displayName is required',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (normalized.length > PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH) {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
code: 'display_name_too_long',
|
|
49
|
+
message: `displayName must be ${PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH} characters or fewer`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (normalized.includes('#')) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
code: 'display_name_reserved_character',
|
|
56
|
+
message: 'displayName must not include #',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (/[\r\n\t]/.test(normalized)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
code: 'display_name_invalid_whitespace',
|
|
63
|
+
message: 'displayName must not include line breaks or tabs',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (/[\u0000-\u001F\u007F]/.test(normalized)) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
code: 'display_name_invalid_character',
|
|
70
|
+
message: 'displayName contains unsupported control characters',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
value: normalized,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function generatePublicIdentityCode({ length = PUBLIC_IDENTITY_CODE_LENGTH } = {}) {
|
|
80
|
+
const targetLength = Number.isInteger(length) && length > 0 ? length : PUBLIC_IDENTITY_CODE_LENGTH;
|
|
81
|
+
const bytes = randomBytes(targetLength);
|
|
82
|
+
let output = '';
|
|
83
|
+
for (let index = 0; index < targetLength; index += 1) {
|
|
84
|
+
output += PUBLIC_IDENTITY_CODE_ALPHABET[bytes[index] % PUBLIC_IDENTITY_CODE_ALPHABET.length];
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function formatPublicIdentityDisplay({ displayName = null, code = null } = {}) {
|
|
90
|
+
const normalizedDisplayName = normalizeText(displayName, null);
|
|
91
|
+
const normalizedCode = normalizeText(code, null);
|
|
92
|
+
if (!normalizedDisplayName || !normalizedCode) return null;
|
|
93
|
+
return `${normalizedDisplayName}#${normalizedCode}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function parsePublicIdentityDisplay(value) {
|
|
97
|
+
const normalized = normalizeText(value, null);
|
|
98
|
+
if (!normalized) return null;
|
|
99
|
+
const hashIndex = normalized.lastIndexOf('#');
|
|
100
|
+
if (hashIndex <= 0 || hashIndex >= normalized.length - 1) return null;
|
|
101
|
+
const displayName = normalizeText(normalized.slice(0, hashIndex), null);
|
|
102
|
+
const code = normalizeText(normalized.slice(hashIndex + 1), null)?.toUpperCase() || null;
|
|
103
|
+
if (!displayName || !code) return null;
|
|
104
|
+
if (displayName.includes('#')) return null;
|
|
105
|
+
return {
|
|
106
|
+
displayName,
|
|
107
|
+
code,
|
|
108
|
+
identity: `${displayName}#${code}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function buildPublicIdentityRecord(input = {}, {
|
|
113
|
+
fallbackDisplayName = null,
|
|
114
|
+
statusFallback = PUBLIC_IDENTITY_STATUS.PENDING,
|
|
115
|
+
now = null,
|
|
116
|
+
} = {}) {
|
|
117
|
+
const source = cloneObject(input);
|
|
118
|
+
const normalizedDisplayName = normalizePublicDisplayName(
|
|
119
|
+
source.displayName,
|
|
120
|
+
{ fallback: normalizePublicDisplayName(fallbackDisplayName, { fallback: null }) },
|
|
121
|
+
);
|
|
122
|
+
const normalizedCode = normalizeText(source.code, null)?.toUpperCase() || null;
|
|
123
|
+
const normalizedStatus = normalizeText(source.status, null);
|
|
124
|
+
const resolvedStatus = normalizedStatus === PUBLIC_IDENTITY_STATUS.READY
|
|
125
|
+
? PUBLIC_IDENTITY_STATUS.READY
|
|
126
|
+
: normalizedStatus === PUBLIC_IDENTITY_STATUS.PENDING
|
|
127
|
+
? PUBLIC_IDENTITY_STATUS.PENDING
|
|
128
|
+
: (normalizedCode && normalizedDisplayName ? PUBLIC_IDENTITY_STATUS.READY : statusFallback);
|
|
129
|
+
const fallbackTimestamp = normalizeIsoTimestamp(now, null);
|
|
130
|
+
const confirmedAt = normalizeIsoTimestamp(source.confirmedAt, null)
|
|
131
|
+
|| (resolvedStatus === PUBLIC_IDENTITY_STATUS.READY ? fallbackTimestamp : null);
|
|
132
|
+
const updatedAt = normalizeIsoTimestamp(source.updatedAt, fallbackTimestamp);
|
|
133
|
+
return {
|
|
134
|
+
displayName: normalizedDisplayName,
|
|
135
|
+
code: normalizedCode,
|
|
136
|
+
status: resolvedStatus,
|
|
137
|
+
confirmedAt,
|
|
138
|
+
updatedAt,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function resolvePublicIdentity(agent = {}) {
|
|
143
|
+
const publicIdentity = buildPublicIdentityRecord(agent?.publicIdentity, {
|
|
144
|
+
fallbackDisplayName: agent?.displayName || agent?.agentId || null,
|
|
145
|
+
now: agent?.createdAt || null,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
...publicIdentity,
|
|
149
|
+
displayIdentity: formatPublicIdentityDisplay(publicIdentity),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function isPublicIdentityReady(agent = {}) {
|
|
154
|
+
return resolvePublicIdentity(agent).status === PUBLIC_IDENTITY_STATUS.READY;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function buildPublicIdentityMissingFields(agent = {}) {
|
|
158
|
+
const publicIdentity = resolvePublicIdentity(agent);
|
|
159
|
+
const missingFields = [];
|
|
160
|
+
if (!publicIdentity.displayName) {
|
|
161
|
+
missingFields.push({
|
|
162
|
+
fieldId: 'displayName',
|
|
163
|
+
label: 'Public Name',
|
|
164
|
+
description: 'A public display name used in Claworld identity surfaces.',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (!publicIdentity.code) {
|
|
168
|
+
missingFields.push({
|
|
169
|
+
fieldId: 'code',
|
|
170
|
+
label: 'Public Code',
|
|
171
|
+
description: 'A system-generated unique suffix used in the public identity.',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return missingFields;
|
|
175
|
+
}
|
|
@@ -189,8 +189,10 @@ export function createAcceptedChatKickoffRuntimeContextForAgent(bundle = {}, {
|
|
|
189
189
|
export function formatAcceptedChatKickoffMessage(bundle = {}, { viewer = 'recipient' } = {}) {
|
|
190
190
|
const normalizedViewer = viewer === 'sender' ? 'sender' : 'recipient';
|
|
191
191
|
const requestContext = bundle.requestContext && typeof bundle.requestContext === 'object' && !Array.isArray(bundle.requestContext)
|
|
192
|
-
? bundle.requestContext
|
|
192
|
+
? cloneJsonObject(bundle.requestContext) || {}
|
|
193
193
|
: {};
|
|
194
|
+
const followUpSessionKey = normalizeText(requestContext.followUp?.sessionKey, null);
|
|
195
|
+
if (requestContext.followUp) delete requestContext.followUp;
|
|
194
196
|
const worldInfo = bundle.worldInfo && typeof bundle.worldInfo === 'object' && !Array.isArray(bundle.worldInfo)
|
|
195
197
|
? bundle.worldInfo
|
|
196
198
|
: null;
|
|
@@ -221,9 +223,13 @@ export function formatAcceptedChatKickoffMessage(bundle = {}, { viewer = 'recipi
|
|
|
221
223
|
viewerInstruction,
|
|
222
224
|
normalizeText(bundle.requestId, null) ? `Accepted episode: ${bundle.requestId}` : null,
|
|
223
225
|
formatStructuredSection('主人想让你做的事情 / 请求上下文', requestContext),
|
|
226
|
+
normalizedViewer === 'sender' && followUpSessionKey
|
|
227
|
+
? `If you decide to report progress back to your owner, use your local session-send tool and send the update to local session ${followUpSessionKey}. Do not report every turn. Report only when there is a meaningful milestone, a clear conclusion or attitude from the peer, a blocker or owner decision is needed, or when the conversation has naturally ended and is ready for a final summary. Keep each update brief with the current status, the key information, and the recommended next step. If no update is needed yet, you may wait.`
|
|
228
|
+
: null,
|
|
224
229
|
formatStructuredSection('世界信息', worldInfo),
|
|
225
230
|
formatStructuredSection('我方信息', selfInfo),
|
|
226
231
|
formatStructuredSection('对方信息', peerInfo),
|
|
232
|
+
'在正常回复中,你可以附带 [[like]] 或 [[dislike]] 来表达你对这段 conversation 的反馈。这个 token 对对方可见,系统只会按当前 conversation 和方向记录第一次有效反馈。',
|
|
227
233
|
normalizedViewer === 'sender'
|
|
228
234
|
? '请你现在直接输出一条自然的开场白。不要解释规则,不要复述这些说明。'
|
|
229
235
|
: '请把对方刚发来的 opener 视为这段对话的第一条 live turn,并决定是否以及如何直接回复。不要解释规则,不要复述这些说明。',
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
CLAWORLD_INSTALLER_BIN_NAME,
|
|
9
9
|
CLAWORLD_INSTALLER_COMMAND,
|
|
10
10
|
CLAWORLD_INSTALLER_PACKAGE_NAME,
|
|
11
|
+
CLAWORLD_UNINSTALL_COMMAND,
|
|
11
12
|
CLAWORLD_UPDATE_COMMAND,
|
|
12
13
|
} from './constants.js';
|
|
13
14
|
import { formatClaworldDoctorReport, runClaworldDoctor } from './doctor.js';
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
DEFAULT_OPENCLAW_BIN,
|
|
16
17
|
DEFAULT_OPENCLAW_CONFIG_PATH,
|
|
17
18
|
runClaworldInstallerInstall,
|
|
19
|
+
runClaworldInstallerUninstall,
|
|
18
20
|
runClaworldInstallerUpdate,
|
|
19
21
|
} from './core.js';
|
|
20
22
|
|
|
@@ -38,6 +40,7 @@ Commands:
|
|
|
38
40
|
install Run the installer-first Claworld setup flow
|
|
39
41
|
doctor Validate the managed Claworld install health
|
|
40
42
|
update Update the tracked Claworld plugin, refresh managed state, and run doctor
|
|
43
|
+
uninstall Safely remove the managed Claworld runtime and uninstall the plugin
|
|
41
44
|
upgrade Alias for update
|
|
42
45
|
help Show this help
|
|
43
46
|
|
|
@@ -74,6 +77,12 @@ Doctor options:
|
|
|
74
77
|
--account-id <id> Managed Claworld account id (default: claworld)
|
|
75
78
|
--agent-id <id> Local OpenClaw agent id bound to claworld (default: main)
|
|
76
79
|
--workspace <path> Optional dedicated workspace path override
|
|
80
|
+
Uninstall options:
|
|
81
|
+
--config <path> OpenClaw config path (default: ${DEFAULT_OPENCLAW_CONFIG_PATH})
|
|
82
|
+
--state-dir <path> Optional OPENCLAW_STATE_DIR for OpenClaw commands
|
|
83
|
+
--openclaw-bin <path> OpenClaw CLI binary (default: ${DEFAULT_OPENCLAW_BIN})
|
|
84
|
+
--account-id <id> Managed Claworld account id (default: claworld)
|
|
85
|
+
--agent-id <id> Local OpenClaw agent id bound to claworld (default: main)
|
|
77
86
|
--json Print machine-readable result
|
|
78
87
|
--help, -h Show this help
|
|
79
88
|
|
|
@@ -81,6 +90,7 @@ Canonical commands:
|
|
|
81
90
|
${CLAWORLD_INSTALLER_COMMAND}
|
|
82
91
|
${CLAWORLD_DOCTOR_COMMAND}
|
|
83
92
|
${CLAWORLD_UPDATE_COMMAND}
|
|
93
|
+
${CLAWORLD_UNINSTALL_COMMAND}
|
|
84
94
|
`);
|
|
85
95
|
}
|
|
86
96
|
|
|
@@ -132,6 +142,13 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
132
142
|
agentId: 'main',
|
|
133
143
|
workspace: null,
|
|
134
144
|
},
|
|
145
|
+
uninstall: {
|
|
146
|
+
openclawBin: env.OPENCLAW_BIN || DEFAULT_OPENCLAW_BIN,
|
|
147
|
+
configPath: expandUserPath(env.OPENCLAW_CONFIG_PATH || DEFAULT_OPENCLAW_CONFIG_PATH, homeDir),
|
|
148
|
+
stateDir: env.OPENCLAW_STATE_DIR ? expandUserPath(env.OPENCLAW_STATE_DIR, homeDir) : null,
|
|
149
|
+
accountId: 'claworld',
|
|
150
|
+
agentId: 'main',
|
|
151
|
+
},
|
|
135
152
|
};
|
|
136
153
|
|
|
137
154
|
if (argv.length === 0) {
|
|
@@ -149,18 +166,21 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
149
166
|
options.install.configPath = expandUserPath(nextValue(remaining, index), homeDir);
|
|
150
167
|
options.update.configPath = options.install.configPath;
|
|
151
168
|
options.doctor.configPath = options.install.configPath;
|
|
169
|
+
options.uninstall.configPath = options.install.configPath;
|
|
152
170
|
index += 1;
|
|
153
171
|
break;
|
|
154
172
|
case '--state-dir':
|
|
155
173
|
options.install.stateDir = expandUserPath(nextValue(remaining, index), homeDir);
|
|
156
174
|
options.update.stateDir = options.install.stateDir;
|
|
157
175
|
options.doctor.stateDir = options.install.stateDir;
|
|
176
|
+
options.uninstall.stateDir = options.install.stateDir;
|
|
158
177
|
index += 1;
|
|
159
178
|
break;
|
|
160
179
|
case '--openclaw-bin':
|
|
161
180
|
options.install.openclawBin = nextValue(remaining, index);
|
|
162
181
|
options.update.openclawBin = options.install.openclawBin;
|
|
163
182
|
options.doctor.openclawBin = options.install.openclawBin;
|
|
183
|
+
options.uninstall.openclawBin = options.install.openclawBin;
|
|
164
184
|
index += 1;
|
|
165
185
|
break;
|
|
166
186
|
case '--server-url':
|
|
@@ -178,12 +198,14 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
178
198
|
options.install.accountId = nextValue(remaining, index);
|
|
179
199
|
options.update.accountId = options.install.accountId;
|
|
180
200
|
options.doctor.accountId = options.install.accountId;
|
|
201
|
+
options.uninstall.accountId = options.install.accountId;
|
|
181
202
|
index += 1;
|
|
182
203
|
break;
|
|
183
204
|
case '--agent-id':
|
|
184
205
|
options.install.agentId = nextValue(remaining, index);
|
|
185
206
|
options.update.agentId = options.install.agentId;
|
|
186
207
|
options.doctor.agentId = options.install.agentId;
|
|
208
|
+
options.uninstall.agentId = options.install.agentId;
|
|
187
209
|
index += 1;
|
|
188
210
|
break;
|
|
189
211
|
case '--workspace':
|
|
@@ -226,7 +248,7 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
226
248
|
}
|
|
227
249
|
}
|
|
228
250
|
|
|
229
|
-
if (!['install', 'update', 'doctor', 'help'].includes(options.command)) {
|
|
251
|
+
if (!['install', 'update', 'uninstall', 'doctor', 'help'].includes(options.command)) {
|
|
230
252
|
throw new Error(`Unknown command: ${options.command}`);
|
|
231
253
|
}
|
|
232
254
|
if (options.command === 'install' && !['npm', 'link', 'copy', 'skip'].includes(options.install.pluginInstallMode)) {
|
|
@@ -304,6 +326,19 @@ function printUpdateSummary(result, doctorResult) {
|
|
|
304
326
|
console.log('');
|
|
305
327
|
}
|
|
306
328
|
|
|
329
|
+
function printUninstallSummary(result) {
|
|
330
|
+
console.log('');
|
|
331
|
+
console.log('Claworld uninstall complete');
|
|
332
|
+
console.log('===========================');
|
|
333
|
+
console.log(`Managed account: ${result.transformed?.backup?.accountId || 'claworld'}`);
|
|
334
|
+
console.log(`OpenClaw version: ${result.host?.version || '(unknown)'}`);
|
|
335
|
+
console.log(`Plugin action: ${result.plugin?.action || 'unknown'}`);
|
|
336
|
+
console.log(`Runtime refresh: ${result.runtimeRefresh?.action || 'unknown'}`);
|
|
337
|
+
console.log(`Config path: ${result.configPath}`);
|
|
338
|
+
console.log(`Backup path: ${result.backupPath || '(unchanged or new file)'}`);
|
|
339
|
+
console.log('');
|
|
340
|
+
}
|
|
341
|
+
|
|
307
342
|
export async function runInstallerCli(argv = process.argv.slice(2), env = process.env) {
|
|
308
343
|
const parsed = parseInstallerCliArgs(argv, env);
|
|
309
344
|
if (parsed.command === 'help') {
|
|
@@ -347,6 +382,16 @@ export async function runInstallerCli(argv = process.argv.slice(2), env = proces
|
|
|
347
382
|
};
|
|
348
383
|
}
|
|
349
384
|
|
|
385
|
+
if (parsed.command === 'uninstall') {
|
|
386
|
+
const result = await runClaworldInstallerUninstall(parsed.uninstall);
|
|
387
|
+
if (parsed.json) {
|
|
388
|
+
console.log(JSON.stringify(result, null, 2));
|
|
389
|
+
} else {
|
|
390
|
+
printUninstallSummary(result);
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
|
|
350
395
|
const result = await runClaworldDoctor(parsed.doctor);
|
|
351
396
|
if (parsed.json) {
|
|
352
397
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -3,4 +3,5 @@ export const CLAWORLD_INSTALLER_PACKAGE_NAME = '@xfxstudio/claworld';
|
|
|
3
3
|
export const CLAWORLD_INSTALLER_COMMAND = 'npx -y @xfxstudio/claworld install';
|
|
4
4
|
export const CLAWORLD_DOCTOR_COMMAND = 'npx -y @xfxstudio/claworld doctor';
|
|
5
5
|
export const CLAWORLD_UPDATE_COMMAND = 'npx -y @xfxstudio/claworld update';
|
|
6
|
+
export const CLAWORLD_UNINSTALL_COMMAND = 'npx -y @xfxstudio/claworld uninstall';
|
|
6
7
|
export const CLAWORLD_OPENCLAW_MIN_HOST_VERSION = '>=2026.3.22';
|