@aptol/openclaw-persona 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/bin/create.mjs +139 -12
  2. package/package.json +1 -1
package/bin/create.mjs CHANGED
@@ -51,6 +51,73 @@ 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
+ async function askServerChannels() {
61
+ const servers = [];
62
+ let addMore = true;
63
+
64
+ while (addMore) {
65
+ const { guildId } = await inquirer.prompt([{
66
+ type: 'input',
67
+ name: 'guildId',
68
+ message: '서버 ID:',
69
+ validate: (v) => /^\d+$/.test(v.trim()) ? true : '숫자만 입력해주세요.',
70
+ }]);
71
+
72
+ const { requireMention } = await inquirer.prompt([{
73
+ type: 'list',
74
+ name: 'requireMention',
75
+ message: `이 서버에서 멘션 필요?`,
76
+ choices: [
77
+ { name: '아니오 — 모든 메시지에 반응', value: false },
78
+ { name: '예 — 멘션(@봇)할 때만 반응', value: true },
79
+ ],
80
+ }]);
81
+
82
+ const channels = [];
83
+ let addChannels = true;
84
+
85
+ while (addChannels) {
86
+ const { channelId } = await inquirer.prompt([{
87
+ type: 'input',
88
+ name: 'channelId',
89
+ message: '채널 ID (비워두면 서버 전체):',
90
+ default: '',
91
+ }]);
92
+
93
+ if (channelId.trim()) {
94
+ channels.push(channelId.trim());
95
+ const { more } = await inquirer.prompt([{
96
+ type: 'confirm',
97
+ name: 'more',
98
+ message: '채널 더 추가?',
99
+ default: false,
100
+ }]);
101
+ addChannels = more;
102
+ } else {
103
+ addChannels = false;
104
+ }
105
+ }
106
+
107
+ servers.push({ guildId: guildId.trim(), requireMention, channels });
108
+
109
+ const { more } = await inquirer.prompt([{
110
+ type: 'confirm',
111
+ name: 'more',
112
+ message: '서버 더 추가?',
113
+ default: false,
114
+ }]);
115
+ addMore = more;
116
+ }
117
+
118
+ return servers;
119
+ }
120
+
54
121
  async function main() {
55
122
  console.log('\n🎭 OpenClaw Persona Creator\n');
56
123
  console.log('나만의 AI 캐릭터를 만들어보세요!\n');
@@ -114,6 +181,24 @@ async function main() {
114
181
  message: 'Discord 봇 토큰 (나중에 설정하려면 빈칸):',
115
182
  default: '',
116
183
  },
184
+ {
185
+ type: 'list',
186
+ name: 'discordPolicyKey',
187
+ message: '디스코드 서버 정책:',
188
+ choices: Object.keys(DISCORD_POLICIES),
189
+ },
190
+ ]);
191
+
192
+ const discordPolicy = DISCORD_POLICIES[answers.discordPolicyKey];
193
+
194
+ // If allowlist, ask for server/channel IDs
195
+ let serverConfigs = [];
196
+ if (discordPolicy === 'allowlist') {
197
+ console.log('\n📋 활동할 서버와 채널을 설정합니다.\n');
198
+ serverConfigs = await askServerChannels();
199
+ }
200
+
201
+ const answers2 = await inquirer.prompt([
117
202
  {
118
203
  type: 'list',
119
204
  name: 'presetKey',
@@ -130,7 +215,7 @@ async function main() {
130
215
  type: 'input',
131
216
  name: 'outputDir',
132
217
  message: '출력 디렉토리:',
133
- default: (ans) => `./output/${ans.name}`,
218
+ default: `./output/${answers.name.trim()}`,
134
219
  },
135
220
  ]);
136
221
 
@@ -144,8 +229,8 @@ async function main() {
144
229
  creator: answers.creator.trim(),
145
230
  };
146
231
 
147
- const preset = PRESET_NAMES[answers.presetKey];
148
- const outputDir = answers.outputDir;
232
+ const preset = PRESET_NAMES[answers2.presetKey];
233
+ const outputDir = answers2.outputDir;
149
234
 
150
235
  // Create output directories
151
236
  mkdirSync(join(outputDir, 'memory'), { recursive: true });
@@ -153,7 +238,6 @@ async function main() {
153
238
 
154
239
  // Generate files
155
240
  if (preset) {
156
- // Preset-based: copy preset files with variable replacement
157
241
  const presetDir = join(ROOT, 'presets', preset);
158
242
  const soulContent = replaceVars(readTemplate(join(presetDir, 'SOUL.md')), vars);
159
243
  const agentsContent = readTemplate(join(presetDir, 'AGENTS.md'));
@@ -163,7 +247,6 @@ async function main() {
163
247
  writeOutput(outputDir, 'AGENTS.md', agentsContent);
164
248
  writeOutput(outputDir, 'IDENTITY.md', identityContent);
165
249
  } else {
166
- // From scratch: use templates
167
250
  const soulContent = replaceVars(readTemplate(join(ROOT, 'templates', 'SOUL.template.md')), vars);
168
251
  const agentsContent = readTemplate(join(ROOT, 'templates', 'AGENTS.template.md'));
169
252
  const identityContent = replaceVars(readTemplate(join(ROOT, 'templates', 'IDENTITY.template.md')), vars);
@@ -183,7 +266,7 @@ async function main() {
183
266
 
184
267
  // Copy selected modules
185
268
  let hasProactiveChat = false;
186
- for (const moduleKey of answers.moduleKeys) {
269
+ for (const moduleKey of answers2.moduleKeys) {
187
270
  const moduleFile = MODULE_MAP[moduleKey];
188
271
  const src = join(ROOT, 'modules', moduleFile);
189
272
  const dest = join(outputDir, 'modules', moduleFile);
@@ -224,21 +307,53 @@ async function main() {
224
307
  `);
225
308
  }
226
309
 
227
- // Copy and customize openclaw config
310
+ // Build openclaw config
228
311
  const config = JSON.parse(readFileSync(join(ROOT, 'config', 'openclaw.template.json'), 'utf-8'));
229
312
  config.agents.defaults.workspace = outputDir;
230
313
  const discordId = answers.discordId.trim();
231
314
  const discordToken = answers.discordToken.trim();
232
- config.channels.discord.token = discordToken || '__DISCORD_BOT_TOKEN__';
315
+ config.channels.discord.token = discordToken || '';
233
316
  config.channels.discord.dmPolicy = 'allowlist';
234
317
  config.channels.discord.allowFrom = [discordId];
235
318
  config.commands = { ownerAllowFrom: [discordId] };
319
+
320
+ // Discord server policy
321
+ if (discordPolicy === 'mention') {
322
+ config.channels.discord.groupPolicy = 'mention';
323
+ } else if (discordPolicy === 'all') {
324
+ config.channels.discord.groupPolicy = 'all';
325
+ } else {
326
+ // allowlist with specific servers/channels
327
+ config.channels.discord.groupPolicy = 'allowlist';
328
+ const guilds = {};
329
+ for (const server of serverConfigs) {
330
+ const guild = {
331
+ requireMention: server.requireMention,
332
+ channels: {},
333
+ };
334
+ if (server.channels.length > 0) {
335
+ for (const chId of server.channels) {
336
+ guild.channels[chId] = {
337
+ allow: true,
338
+ requireMention: server.requireMention,
339
+ enabled: true,
340
+ };
341
+ }
342
+ } else {
343
+ // No specific channels = server-wide (empty channels object means all channels)
344
+ guild.channels = {};
345
+ }
346
+ guilds[server.guildId] = guild;
347
+ }
348
+ config.channels.discord.guilds = guilds;
349
+ }
350
+
236
351
  writeOutput(outputDir, 'openclaw.json', JSON.stringify(config, null, 2) + '\n');
237
352
 
238
353
  // Create empty .gitkeep in memory folder
239
354
  writeFileSync(join(outputDir, 'memory', '.gitkeep'), '', 'utf-8');
240
355
 
241
- console.log(`\n✅ ${vars.name} 생성 완료! openclaw start로 시작하세요.`);
356
+ console.log(`\n✅ ${vars.name} 생성 완료!`);
242
357
  console.log(`📁 위치: ${outputDir}`);
243
358
  console.log('\n생성된 파일:');
244
359
  console.log(' - SOUL.md (캐릭터 영혼)');
@@ -248,14 +363,26 @@ async function main() {
248
363
  console.log(' - MEMORY.md (장기 기억)');
249
364
  console.log(' - openclaw.json (설정)');
250
365
  console.log(' - memory/ (일별 기억 폴더)');
251
- if (answers.moduleKeys.length > 0) {
366
+ if (answers2.moduleKeys.length > 0) {
252
367
  console.log(' - modules/ (선택한 시스템 모듈)');
253
368
  }
254
- console.log('\n다음 단계:');
369
+
370
+ console.log('\n🚀 구동 방법:');
371
+ if (discordPolicy === 'mention') {
372
+ console.log(' 서버 정책: 멘션(@봇) 시에만 반응');
373
+ } else if (discordPolicy === 'all') {
374
+ console.log(' 서버 정책: 모든 서버에서 항상 반응');
375
+ } else {
376
+ console.log(` 서버 정책: ${serverConfigs.length}개 서버 허용목록`);
377
+ }
378
+ console.log('');
255
379
  console.log(' 1. openclaw.json에서 API 키와 Discord 토큰을 설정하세요');
256
380
  console.log(' 2. USER.md에 주인 정보를 추가하세요');
257
381
  console.log(' 3. SOUL.md를 원하는 대로 커스텀하세요');
258
- console.log(' 4. openclaw start로 시작!\n');
382
+ console.log(' 4. 구동:');
383
+ console.log(` $env:OPENCLAW_HOME = "${outputDir}"`);
384
+ console.log(' openclaw gateway run');
385
+ console.log('');
259
386
  }
260
387
 
261
388
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aptol/openclaw-persona",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Create your own AI persona for OpenClaw — interactive CLI character creator with presets, modules, and full customization",
5
5
  "type": "module",
6
6
  "bin": {