@aptol/openclaw-persona 1.0.0 → 1.2.1
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/bin/create.mjs +369 -76
- package/package.json +1 -1
package/bin/create.mjs
CHANGED
|
@@ -51,11 +51,87 @@ const MODULE_MAP = {
|
|
|
51
51
|
'선제 대화 (Proactive Chat)': 'proactive-chat.md',
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const DISCORD_POLICIES = {
|
|
55
|
+
'모든 서버에서 멘션(@봇) 시 반응 (추천, 설정 불필요)': 'mention',
|
|
56
|
+
'모든 서버에서 항상 반응': 'all',
|
|
57
|
+
'특정 서버/채널만 허용 (ID 직접 입력)': 'allowlist',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const AI_MODELS = {
|
|
61
|
+
'Claude Opus 4 (최고 성능, 비용 높음)': 'anthropic/claude-opus-4-6',
|
|
62
|
+
'Claude Sonnet 4 (균형, 추천)': 'anthropic/claude-sonnet-4-20250514',
|
|
63
|
+
'Claude Sonnet 4.5 (고성능 + 빠름)': 'anthropic/claude-sonnet-4-5-20250514',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
async function askServerChannels() {
|
|
67
|
+
const servers = [];
|
|
68
|
+
let addMore = true;
|
|
69
|
+
|
|
70
|
+
while (addMore) {
|
|
71
|
+
const { guildId } = await inquirer.prompt([{
|
|
72
|
+
type: 'input',
|
|
73
|
+
name: 'guildId',
|
|
74
|
+
message: '서버 ID:',
|
|
75
|
+
validate: (v) => /^\d+$/.test(v.trim()) ? true : '숫자만 입력해주세요.',
|
|
76
|
+
}]);
|
|
77
|
+
|
|
78
|
+
const { requireMention } = await inquirer.prompt([{
|
|
79
|
+
type: 'list',
|
|
80
|
+
name: 'requireMention',
|
|
81
|
+
message: `이 서버에서 멘션 필요?`,
|
|
82
|
+
choices: [
|
|
83
|
+
{ name: '아니오 — 모든 메시지에 반응', value: false },
|
|
84
|
+
{ name: '예 — 멘션(@봇)할 때만 반응', value: true },
|
|
85
|
+
],
|
|
86
|
+
}]);
|
|
87
|
+
|
|
88
|
+
const channels = [];
|
|
89
|
+
let addChannels = true;
|
|
90
|
+
|
|
91
|
+
while (addChannels) {
|
|
92
|
+
const { channelId } = await inquirer.prompt([{
|
|
93
|
+
type: 'input',
|
|
94
|
+
name: 'channelId',
|
|
95
|
+
message: '채널 ID (비워두면 서버 전체):',
|
|
96
|
+
default: '',
|
|
97
|
+
}]);
|
|
98
|
+
|
|
99
|
+
if (channelId.trim()) {
|
|
100
|
+
channels.push(channelId.trim());
|
|
101
|
+
const { more } = await inquirer.prompt([{
|
|
102
|
+
type: 'confirm',
|
|
103
|
+
name: 'more',
|
|
104
|
+
message: '채널 더 추가?',
|
|
105
|
+
default: false,
|
|
106
|
+
}]);
|
|
107
|
+
addChannels = more;
|
|
108
|
+
} else {
|
|
109
|
+
addChannels = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
servers.push({ guildId: guildId.trim(), requireMention, channels });
|
|
114
|
+
|
|
115
|
+
const { more } = await inquirer.prompt([{
|
|
116
|
+
type: 'confirm',
|
|
117
|
+
name: 'more',
|
|
118
|
+
message: '서버 더 추가?',
|
|
119
|
+
default: false,
|
|
120
|
+
}]);
|
|
121
|
+
addMore = more;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return servers;
|
|
125
|
+
}
|
|
126
|
+
|
|
54
127
|
async function main() {
|
|
55
128
|
console.log('\n🎭 OpenClaw Persona Creator\n');
|
|
56
129
|
console.log('나만의 AI 캐릭터를 만들어보세요!\n');
|
|
57
130
|
|
|
58
|
-
|
|
131
|
+
// ─── 1. 캐릭터 기본 정보 ───
|
|
132
|
+
console.log('━━━ 1/5: 캐릭터 정보 ━━━\n');
|
|
133
|
+
|
|
134
|
+
const charAnswers = await inquirer.prompt([
|
|
59
135
|
{
|
|
60
136
|
type: 'input',
|
|
61
137
|
name: 'name',
|
|
@@ -98,10 +174,106 @@ async function main() {
|
|
|
98
174
|
message: '만든 사람 이름:',
|
|
99
175
|
validate: (v) => v.trim() ? true : '이름을 입력해주세요.',
|
|
100
176
|
},
|
|
177
|
+
{
|
|
178
|
+
type: 'list',
|
|
179
|
+
name: 'presetKey',
|
|
180
|
+
message: '프리셋 베이스:',
|
|
181
|
+
choices: Object.keys(PRESET_NAMES),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'checkbox',
|
|
185
|
+
name: 'moduleKeys',
|
|
186
|
+
message: '모듈 선택 (스페이스바로 선택):',
|
|
187
|
+
choices: Object.keys(MODULE_MAP),
|
|
188
|
+
},
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
// ─── 2. AI 모델 설정 ───
|
|
192
|
+
console.log('\n━━━ 2/5: AI 모델 설정 ━━━\n');
|
|
193
|
+
|
|
194
|
+
const modelAnswers = await inquirer.prompt([
|
|
195
|
+
{
|
|
196
|
+
type: 'list',
|
|
197
|
+
name: 'modelKey',
|
|
198
|
+
message: 'AI 모델 선택:',
|
|
199
|
+
choices: Object.keys(AI_MODELS),
|
|
200
|
+
},
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
// ─── 3. API 키 설정 ───
|
|
204
|
+
console.log('\n━━━ 3/5: API 키 설정 ━━━\n');
|
|
205
|
+
|
|
206
|
+
const authModeAnswers = await inquirer.prompt([{
|
|
207
|
+
type: 'list',
|
|
208
|
+
name: 'anthropicAuth',
|
|
209
|
+
message: 'Anthropic 인증 방식:',
|
|
210
|
+
choices: [
|
|
211
|
+
{ name: 'API 키 직접 입력 (sk-ant-...)', value: 'token' },
|
|
212
|
+
{ name: 'Claude Max/Pro 구독 연결 (OAuth, 브라우저 인증)', value: 'oauth' },
|
|
213
|
+
{ name: '나중에 설정', value: 'skip' },
|
|
214
|
+
],
|
|
215
|
+
}]);
|
|
216
|
+
|
|
217
|
+
let anthropicKey = '';
|
|
218
|
+
let anthropicOAuth = false;
|
|
219
|
+
|
|
220
|
+
if (authModeAnswers.anthropicAuth === 'token') {
|
|
221
|
+
const { key } = await inquirer.prompt([{
|
|
222
|
+
type: 'password',
|
|
223
|
+
name: 'key',
|
|
224
|
+
message: 'Anthropic API 키 (sk-ant-...):',
|
|
225
|
+
mask: '*',
|
|
226
|
+
}]);
|
|
227
|
+
anthropicKey = key.trim();
|
|
228
|
+
} else if (authModeAnswers.anthropicAuth === 'oauth') {
|
|
229
|
+
anthropicOAuth = true;
|
|
230
|
+
console.log('\n📌 구독 연결은 캐릭터 생성 후 아래 명령어로 완료하세요:');
|
|
231
|
+
console.log(' $env:OPENCLAW_HOME = "<출력 디렉토리>"');
|
|
232
|
+
console.log(' openclaw auth login\n');
|
|
233
|
+
console.log(' 브라우저가 열리면 Claude 계정으로 로그인하세요.\n');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log('💡 빈칸으로 넘기면 나중에 openclaw.json에서 수동 설정 가능\n');
|
|
237
|
+
|
|
238
|
+
const apiAnswers = await inquirer.prompt([
|
|
239
|
+
{
|
|
240
|
+
type: 'password',
|
|
241
|
+
name: 'openaiKey',
|
|
242
|
+
message: 'OpenAI API 키 (메모리 검색용, sk-...):',
|
|
243
|
+
mask: '*',
|
|
244
|
+
default: '',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
type: 'password',
|
|
248
|
+
name: 'braveKey',
|
|
249
|
+
message: 'Brave Search API 키 (웹 검색용):',
|
|
250
|
+
mask: '*',
|
|
251
|
+
default: '',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: 'password',
|
|
255
|
+
name: 'elevenlabsKey',
|
|
256
|
+
message: 'ElevenLabs API 키 (음성 TTS용, 선택):',
|
|
257
|
+
mask: '*',
|
|
258
|
+
default: '',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
type: 'password',
|
|
262
|
+
name: 'deepgramKey',
|
|
263
|
+
message: 'Deepgram API 키 (음성 STT용, 선택):',
|
|
264
|
+
mask: '*',
|
|
265
|
+
default: '',
|
|
266
|
+
},
|
|
267
|
+
]);
|
|
268
|
+
|
|
269
|
+
// ─── 4. 디스코드 설정 ───
|
|
270
|
+
console.log('\n━━━ 4/5: 디스코드 설정 ━━━\n');
|
|
271
|
+
|
|
272
|
+
const discordAnswers = await inquirer.prompt([
|
|
101
273
|
{
|
|
102
274
|
type: 'input',
|
|
103
275
|
name: 'discordId',
|
|
104
|
-
message: 'Discord 사용자 ID (
|
|
276
|
+
message: 'Discord 사용자 ID (숫자, 주인):',
|
|
105
277
|
validate: (v) => {
|
|
106
278
|
if (!v.trim()) return '디스코드 ID를 입력해주세요.';
|
|
107
279
|
if (!/^\d+$/.test(v.trim())) return '숫자만 입력해주세요.';
|
|
@@ -109,89 +281,89 @@ async function main() {
|
|
|
109
281
|
},
|
|
110
282
|
},
|
|
111
283
|
{
|
|
112
|
-
type: '
|
|
284
|
+
type: 'password',
|
|
113
285
|
name: 'discordToken',
|
|
114
|
-
message: 'Discord 봇
|
|
286
|
+
message: 'Discord 봇 토큰:',
|
|
287
|
+
mask: '*',
|
|
115
288
|
default: '',
|
|
116
289
|
},
|
|
117
290
|
{
|
|
118
291
|
type: 'list',
|
|
119
|
-
name: '
|
|
120
|
-
message: '
|
|
121
|
-
choices: Object.keys(
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
type: 'checkbox',
|
|
125
|
-
name: 'moduleKeys',
|
|
126
|
-
message: '모듈 선택 (스페이스바로 선택):',
|
|
127
|
-
choices: Object.keys(MODULE_MAP),
|
|
292
|
+
name: 'discordPolicyKey',
|
|
293
|
+
message: '디스코드 서버 정책:',
|
|
294
|
+
choices: Object.keys(DISCORD_POLICIES),
|
|
128
295
|
},
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
const discordPolicy = DISCORD_POLICIES[discordAnswers.discordPolicyKey];
|
|
299
|
+
|
|
300
|
+
let serverConfigs = [];
|
|
301
|
+
if (discordPolicy === 'allowlist') {
|
|
302
|
+
console.log('\n📋 활동할 서버와 채널을 설정합니다.\n');
|
|
303
|
+
serverConfigs = await askServerChannels();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ─── 5. 출력 설정 ───
|
|
307
|
+
console.log('\n━━━ 5/5: 출력 설정 ━━━\n');
|
|
308
|
+
|
|
309
|
+
const outputAnswers = await inquirer.prompt([
|
|
129
310
|
{
|
|
130
311
|
type: 'input',
|
|
131
312
|
name: 'outputDir',
|
|
132
313
|
message: '출력 디렉토리:',
|
|
133
|
-
default:
|
|
314
|
+
default: `./output/${charAnswers.name.trim()}`,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
type: 'input',
|
|
318
|
+
name: 'gatewayPort',
|
|
319
|
+
message: '게이트웨이 포트 (기본 18789, 다른 봇 있으면 변경):',
|
|
320
|
+
default: '18789',
|
|
321
|
+
validate: (v) => /^\d+$/.test(v.trim()) ? true : '숫자만 입력해주세요.',
|
|
134
322
|
},
|
|
135
323
|
]);
|
|
136
324
|
|
|
325
|
+
// ─── Build ───
|
|
326
|
+
|
|
137
327
|
const vars = {
|
|
138
|
-
name:
|
|
139
|
-
gender:
|
|
140
|
-
speechStyle: SPEECH_STYLES[
|
|
141
|
-
personality:
|
|
142
|
-
likes:
|
|
143
|
-
dislikes:
|
|
144
|
-
creator:
|
|
328
|
+
name: charAnswers.name.trim(),
|
|
329
|
+
gender: charAnswers.gender,
|
|
330
|
+
speechStyle: SPEECH_STYLES[charAnswers.speechStyleKey],
|
|
331
|
+
personality: charAnswers.personality.trim(),
|
|
332
|
+
likes: charAnswers.likes.trim(),
|
|
333
|
+
dislikes: charAnswers.dislikes.trim(),
|
|
334
|
+
creator: charAnswers.creator.trim(),
|
|
145
335
|
};
|
|
146
336
|
|
|
147
|
-
const preset = PRESET_NAMES[
|
|
148
|
-
const outputDir =
|
|
337
|
+
const preset = PRESET_NAMES[charAnswers.presetKey];
|
|
338
|
+
const outputDir = outputAnswers.outputDir;
|
|
149
339
|
|
|
150
340
|
// Create output directories
|
|
151
341
|
mkdirSync(join(outputDir, 'memory'), { recursive: true });
|
|
152
342
|
mkdirSync(join(outputDir, 'modules'), { recursive: true });
|
|
153
343
|
|
|
154
|
-
// Generate files
|
|
344
|
+
// Generate character files
|
|
155
345
|
if (preset) {
|
|
156
|
-
// Preset-based: copy preset files with variable replacement
|
|
157
346
|
const presetDir = join(ROOT, 'presets', preset);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
writeOutput(outputDir, 'SOUL.md', soulContent);
|
|
163
|
-
writeOutput(outputDir, 'AGENTS.md', agentsContent);
|
|
164
|
-
writeOutput(outputDir, 'IDENTITY.md', identityContent);
|
|
347
|
+
writeOutput(outputDir, 'SOUL.md', replaceVars(readTemplate(join(presetDir, 'SOUL.md')), vars));
|
|
348
|
+
writeOutput(outputDir, 'AGENTS.md', readTemplate(join(presetDir, 'AGENTS.md')));
|
|
349
|
+
writeOutput(outputDir, 'IDENTITY.md', replaceVars(readTemplate(join(presetDir, 'IDENTITY.md')), vars));
|
|
165
350
|
} else {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const identityContent = replaceVars(readTemplate(join(ROOT, 'templates', 'IDENTITY.template.md')), vars);
|
|
170
|
-
|
|
171
|
-
writeOutput(outputDir, 'SOUL.md', soulContent);
|
|
172
|
-
writeOutput(outputDir, 'AGENTS.md', agentsContent);
|
|
173
|
-
writeOutput(outputDir, 'IDENTITY.md', identityContent);
|
|
351
|
+
writeOutput(outputDir, 'SOUL.md', replaceVars(readTemplate(join(ROOT, 'templates', 'SOUL.template.md')), vars));
|
|
352
|
+
writeOutput(outputDir, 'AGENTS.md', readTemplate(join(ROOT, 'templates', 'AGENTS.template.md')));
|
|
353
|
+
writeOutput(outputDir, 'IDENTITY.md', replaceVars(readTemplate(join(ROOT, 'templates', 'IDENTITY.template.md')), vars));
|
|
174
354
|
}
|
|
175
355
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
writeOutput(outputDir, 'USER.md', userContent);
|
|
179
|
-
|
|
180
|
-
// MEMORY.md
|
|
181
|
-
const memoryContent = replaceVars(readTemplate(join(ROOT, 'templates', 'MEMORY.template.md')), vars);
|
|
182
|
-
writeOutput(outputDir, 'MEMORY.md', memoryContent);
|
|
356
|
+
writeOutput(outputDir, 'USER.md', replaceVars(readTemplate(join(ROOT, 'templates', 'USER.template.md')), vars));
|
|
357
|
+
writeOutput(outputDir, 'MEMORY.md', replaceVars(readTemplate(join(ROOT, 'templates', 'MEMORY.template.md')), vars));
|
|
183
358
|
|
|
184
359
|
// Copy selected modules
|
|
185
360
|
let hasProactiveChat = false;
|
|
186
|
-
for (const moduleKey of
|
|
361
|
+
for (const moduleKey of charAnswers.moduleKeys) {
|
|
187
362
|
const moduleFile = MODULE_MAP[moduleKey];
|
|
188
|
-
|
|
189
|
-
const dest = join(outputDir, 'modules', moduleFile);
|
|
190
|
-
copyFileSync(src, dest);
|
|
363
|
+
copyFileSync(join(ROOT, 'modules', moduleFile), join(outputDir, 'modules', moduleFile));
|
|
191
364
|
if (moduleFile === 'proactive-chat.md') hasProactiveChat = true;
|
|
192
365
|
}
|
|
193
366
|
|
|
194
|
-
// If proactive-chat module selected, create HEARTBEAT.md
|
|
195
367
|
if (hasProactiveChat) {
|
|
196
368
|
writeOutput(outputDir, 'HEARTBEAT.md', `# HEARTBEAT.md
|
|
197
369
|
|
|
@@ -224,38 +396,159 @@ async function main() {
|
|
|
224
396
|
`);
|
|
225
397
|
}
|
|
226
398
|
|
|
227
|
-
//
|
|
399
|
+
// ─── Build openclaw.json ───
|
|
400
|
+
|
|
228
401
|
const config = JSON.parse(readFileSync(join(ROOT, 'config', 'openclaw.template.json'), 'utf-8'));
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
402
|
+
const discordId = discordAnswers.discordId.trim();
|
|
403
|
+
const port = parseInt(outputAnswers.gatewayPort.trim());
|
|
404
|
+
|
|
405
|
+
// Model
|
|
406
|
+
config.agents.defaults.model.primary = AI_MODELS[modelAnswers.modelKey];
|
|
407
|
+
|
|
408
|
+
// Anthropic auth
|
|
409
|
+
if (anthropicOAuth) {
|
|
410
|
+
config.auth.profiles['anthropic:default'] = {
|
|
411
|
+
provider: 'anthropic',
|
|
412
|
+
mode: 'oauth',
|
|
413
|
+
};
|
|
414
|
+
} else if (anthropicKey) {
|
|
415
|
+
config.auth.profiles['anthropic:default'].token = anthropicKey;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// OpenAI (memory search)
|
|
419
|
+
if (apiAnswers.openaiKey.trim()) {
|
|
420
|
+
config.agents.defaults.memorySearch.remote = { apiKey: apiAnswers.openaiKey.trim() };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Brave Search
|
|
424
|
+
if (apiAnswers.braveKey.trim()) {
|
|
425
|
+
if (!config.plugins) config.plugins = { entries: {} };
|
|
426
|
+
config.plugins.entries = config.plugins.entries || {};
|
|
427
|
+
config.plugins.entries.brave = {
|
|
428
|
+
enabled: true,
|
|
429
|
+
config: { webSearch: { apiKey: apiAnswers.braveKey.trim() } },
|
|
430
|
+
};
|
|
431
|
+
if (!config.tools) config.tools = {};
|
|
432
|
+
config.tools.web = { search: { provider: 'brave' } };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ElevenLabs
|
|
436
|
+
if (apiAnswers.elevenlabsKey.trim()) {
|
|
437
|
+
if (!config.env) config.env = {};
|
|
438
|
+
if (!config.env.vars) config.env.vars = {};
|
|
439
|
+
config.env.vars.ELEVENLABS_API_KEY = apiAnswers.elevenlabsKey.trim();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Deepgram
|
|
443
|
+
if (apiAnswers.deepgramKey.trim()) {
|
|
444
|
+
if (!config.env) config.env = {};
|
|
445
|
+
if (!config.env.vars) config.env.vars = {};
|
|
446
|
+
config.env.vars.DEEPGRAM_API_KEY = apiAnswers.deepgramKey.trim();
|
|
447
|
+
config.env.vars.DEEPGRAM_MODEL = 'nova-3';
|
|
448
|
+
config.env.vars.DEEPGRAM_LANGUAGE = 'ko';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Discord
|
|
452
|
+
config.channels.discord.token = discordAnswers.discordToken.trim() || '';
|
|
233
453
|
config.channels.discord.dmPolicy = 'allowlist';
|
|
234
454
|
config.channels.discord.allowFrom = [discordId];
|
|
235
|
-
config.commands = {
|
|
236
|
-
|
|
455
|
+
config.commands = {
|
|
456
|
+
native: 'auto',
|
|
457
|
+
nativeSkills: 'auto',
|
|
458
|
+
debug: true,
|
|
459
|
+
restart: true,
|
|
460
|
+
ownerAllowFrom: [discordId],
|
|
461
|
+
ownerDisplay: 'raw',
|
|
462
|
+
};
|
|
237
463
|
|
|
238
|
-
//
|
|
464
|
+
// Discord server policy
|
|
465
|
+
if (discordPolicy === 'mention') {
|
|
466
|
+
config.channels.discord.groupPolicy = 'mention';
|
|
467
|
+
} else if (discordPolicy === 'all') {
|
|
468
|
+
config.channels.discord.groupPolicy = 'all';
|
|
469
|
+
} else {
|
|
470
|
+
config.channels.discord.groupPolicy = 'allowlist';
|
|
471
|
+
const guilds = {};
|
|
472
|
+
for (const server of serverConfigs) {
|
|
473
|
+
const guild = { requireMention: server.requireMention, channels: {} };
|
|
474
|
+
for (const chId of server.channels) {
|
|
475
|
+
guild.channels[chId] = { allow: true, requireMention: server.requireMention, enabled: true };
|
|
476
|
+
}
|
|
477
|
+
guilds[server.guildId] = guild;
|
|
478
|
+
}
|
|
479
|
+
config.channels.discord.guilds = guilds;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Gateway
|
|
483
|
+
config.gateway.port = port;
|
|
484
|
+
|
|
485
|
+
// Discord plugin
|
|
486
|
+
if (!config.plugins) config.plugins = { entries: {} };
|
|
487
|
+
config.plugins.entries.discord = { enabled: true, config: {} };
|
|
488
|
+
|
|
489
|
+
// Streaming
|
|
490
|
+
config.channels.discord.streaming = 'partial';
|
|
491
|
+
config.channels.discord.historyLimit = 16;
|
|
492
|
+
config.channels.discord.intents = { presence: true };
|
|
493
|
+
config.channels.discord.actions = {
|
|
494
|
+
reactions: true, stickers: true, emojiUploads: true, stickerUploads: true,
|
|
495
|
+
polls: true, permissions: true, messages: true, threads: true, pins: true,
|
|
496
|
+
search: true, memberInfo: true, roleInfo: true, roles: true, channelInfo: true,
|
|
497
|
+
voiceStatus: true, events: true, moderation: true, channels: true, presence: true,
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
writeOutput(outputDir, 'openclaw.json', JSON.stringify(config, null, 2) + '\n');
|
|
239
501
|
writeFileSync(join(outputDir, 'memory', '.gitkeep'), '', 'utf-8');
|
|
240
502
|
|
|
241
|
-
|
|
503
|
+
// ─── Summary ───
|
|
504
|
+
|
|
505
|
+
const configured = [];
|
|
506
|
+
const notConfigured = [];
|
|
507
|
+
|
|
508
|
+
if (anthropicKey) configured.push('Anthropic (API 키)');
|
|
509
|
+
else if (anthropicOAuth) configured.push('Anthropic (OAuth 구독 — openclaw auth login 필요)');
|
|
510
|
+
else notConfigured.push('Anthropic 인증');
|
|
511
|
+
|
|
512
|
+
if (apiAnswers.openaiKey.trim()) configured.push('OpenAI (메모리 검색)');
|
|
513
|
+
else notConfigured.push('OpenAI API 키 (메모리 검색)');
|
|
514
|
+
|
|
515
|
+
if (apiAnswers.braveKey.trim()) configured.push('Brave Search (웹 검색)');
|
|
516
|
+
else notConfigured.push('Brave Search API 키 (웹 검색)');
|
|
517
|
+
|
|
518
|
+
if (apiAnswers.elevenlabsKey.trim()) configured.push('ElevenLabs (음성 TTS)');
|
|
519
|
+
if (apiAnswers.deepgramKey.trim()) configured.push('Deepgram (음성 STT)');
|
|
520
|
+
|
|
521
|
+
if (discordAnswers.discordToken.trim()) configured.push('Discord 봇 토큰');
|
|
522
|
+
else notConfigured.push('Discord 봇 토큰');
|
|
523
|
+
|
|
524
|
+
console.log(`\n✅ ${vars.name} 생성 완료!`);
|
|
242
525
|
console.log(`📁 위치: ${outputDir}`);
|
|
243
|
-
|
|
244
|
-
console.log('
|
|
245
|
-
console.log('
|
|
246
|
-
console.log('
|
|
247
|
-
console.log('
|
|
248
|
-
|
|
249
|
-
console.log('
|
|
250
|
-
console.log('
|
|
251
|
-
if (
|
|
252
|
-
|
|
526
|
+
|
|
527
|
+
console.log('\n📋 생성된 파일:');
|
|
528
|
+
console.log(' SOUL.md / AGENTS.md / IDENTITY.md / USER.md / MEMORY.md / openclaw.json');
|
|
529
|
+
if (charAnswers.moduleKeys.length > 0) console.log(' modules/ (선택한 시스템 모듈)');
|
|
530
|
+
if (hasProactiveChat) console.log(' HEARTBEAT.md (선제 대화)');
|
|
531
|
+
|
|
532
|
+
console.log('\n🔑 API 설정 현황:');
|
|
533
|
+
if (configured.length > 0) console.log(' ✅ ' + configured.join(', '));
|
|
534
|
+
if (notConfigured.length > 0) console.log(' ❌ 미설정: ' + notConfigured.join(', '));
|
|
535
|
+
|
|
536
|
+
console.log(`\n🤖 모델: ${AI_MODELS[modelAnswers.modelKey]}`);
|
|
537
|
+
|
|
538
|
+
if (discordPolicy === 'mention') console.log('🏠 서버 정책: 멘션(@봇) 시에만 반응');
|
|
539
|
+
else if (discordPolicy === 'all') console.log('🏠 서버 정책: 모든 서버에서 항상 반응');
|
|
540
|
+
else console.log(`🏠 서버 정책: ${serverConfigs.length}개 서버 허용목록`);
|
|
541
|
+
|
|
542
|
+
console.log(`🌐 게이트웨이 포트: ${port}`);
|
|
543
|
+
|
|
544
|
+
console.log('\n🚀 구동 방법:');
|
|
545
|
+
console.log(` $env:OPENCLAW_HOME = "${outputDir}"`);
|
|
546
|
+
console.log(' openclaw gateway run');
|
|
547
|
+
|
|
548
|
+
if (notConfigured.length > 0) {
|
|
549
|
+
console.log('\n⚠️ 미설정 항목은 openclaw.json에서 직접 수정하세요.');
|
|
253
550
|
}
|
|
254
|
-
console.log('
|
|
255
|
-
console.log(' 1. openclaw.json에서 API 키와 Discord 토큰을 설정하세요');
|
|
256
|
-
console.log(' 2. USER.md에 주인 정보를 추가하세요');
|
|
257
|
-
console.log(' 3. SOUL.md를 원하는 대로 커스텀하세요');
|
|
258
|
-
console.log(' 4. openclaw start로 시작!\n');
|
|
551
|
+
console.log('');
|
|
259
552
|
}
|
|
260
553
|
|
|
261
554
|
main().catch((err) => {
|
package/package.json
CHANGED