dantelabs-agentic-school 1.4.0 → 1.5.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/.claude-plugin/marketplace.json +13 -2
- package/README.md +83 -4
- package/cli/src/commands/install.js +90 -19
- package/cli/src/commands/uninstall.js +58 -11
- package/cli/src/i18n/locales/en.js +7 -0
- package/cli/src/i18n/locales/ko.js +7 -0
- package/cli/src/lib/installer.js +42 -22
- package/cli/src/lib/platforms.js +91 -0
- package/package.json +1 -1
- package/plugins/gcp/gcp-openclaw/commands/deploy-openclaw.md +86 -0
- package/plugins/gcp/gcp-openclaw/plugin.json +9 -0
- package/plugins/gcp/gcp-openclaw/skills/gcp-openclaw/SKILL.md +216 -0
|
@@ -137,12 +137,20 @@
|
|
|
137
137
|
"version": "1.0.0",
|
|
138
138
|
"source": "./plugins/data-science/model-deployment",
|
|
139
139
|
"category": "data-science"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"name": "gcp-openclaw",
|
|
143
|
+
"description": "GCP VM 인스턴스에 OpenClaw를 배포하는 가이드 및 자동화 도구",
|
|
144
|
+
"version": "1.0.0",
|
|
145
|
+
"source": "./plugins/gcp/gcp-openclaw",
|
|
146
|
+
"category": "gcp"
|
|
140
147
|
}
|
|
141
148
|
],
|
|
142
149
|
"categories": [
|
|
143
150
|
"common",
|
|
144
151
|
"marketing",
|
|
145
|
-
"data-science"
|
|
152
|
+
"data-science",
|
|
153
|
+
"gcp"
|
|
146
154
|
],
|
|
147
155
|
"tags": [
|
|
148
156
|
"agentic-business",
|
|
@@ -165,6 +173,9 @@
|
|
|
165
173
|
"model-monitoring",
|
|
166
174
|
"model-deployment",
|
|
167
175
|
"imbalance-handling",
|
|
168
|
-
"workflow-automation"
|
|
176
|
+
"workflow-automation",
|
|
177
|
+
"gcp",
|
|
178
|
+
"openclaw",
|
|
179
|
+
"deployment"
|
|
169
180
|
]
|
|
170
181
|
}
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
이 프로젝트는 **GTM Agents 스타일의 전략 에이전트**와 **실제 생성/분석 도구**를 통합하여, **엔드투엔드 비즈니스 자동화**를 제공합니다.
|
|
8
8
|
|
|
9
|
-
현재 **마케팅 자동화**(8개)
|
|
9
|
+
현재 **마케팅 자동화**(8개), **데이터 사이언스**(9개), **GCP 클라우드**(1개) 플러그인이 구현되어 있으며, 향후 **일반 비즈니스** 등으로 확장될 예정입니다.
|
|
10
10
|
|
|
11
11
|
## 지원 영역
|
|
12
12
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
| --- | --- | --- | --- |
|
|
15
15
|
| 🎯 Marketing | ✅ 구현완료 | 브랜드 분석 → 크리에이티브 제작 | 8개 |
|
|
16
16
|
| 📊 Data Science | ✅ 구현완료 | 데이터 분석 → 모델 학습 → 배포 | 9개 |
|
|
17
|
+
| ☁️ GCP | ✅ 구현완료 | GCP VM 배포 가이드 및 자동화 | 1개 |
|
|
17
18
|
| 💼 Business Ops | 🔜 예정 | 워크플로우 → 자동화 → 리포팅 | - |
|
|
18
19
|
|
|
19
20
|
## 특징
|
|
@@ -54,6 +55,7 @@ npx dantelabs-agentic-school uninstall brand-analytics
|
|
|
54
55
|
| 옵션 | 설명 |
|
|
55
56
|
| --- | --- |
|
|
56
57
|
| `--path, -p` | 설치 경로 (기본: 현재 디렉토리) |
|
|
58
|
+
| `--target, -t` | 타겟 플랫폼 (claude, gemini, antigravity, codex, opencode, agents) |
|
|
57
59
|
| `--force, -f` | 기존 파일 덮어쓰기 |
|
|
58
60
|
| `--no-common` | common 유틸리티 제외 |
|
|
59
61
|
| `--dry-run` | 미리보기 (실제 설치 안함) |
|
|
@@ -61,6 +63,40 @@ npx dantelabs-agentic-school uninstall brand-analytics
|
|
|
61
63
|
| `-v, --verbose` | 상세 정보 표시 |
|
|
62
64
|
| `-l, --lang` | 언어 설정 (en, ko) - 기본: en |
|
|
63
65
|
|
|
66
|
+
### 멀티플랫폼 설치
|
|
67
|
+
|
|
68
|
+
`--target` 옵션으로 다양한 AI 코딩 도구에 플러그인을 설치할 수 있습니다.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Claude Code (기본값, --target 생략 가능)
|
|
72
|
+
npx dantelabs-agentic-school install gcp-openclaw
|
|
73
|
+
|
|
74
|
+
# Gemini CLI
|
|
75
|
+
npx dantelabs-agentic-school install gcp-openclaw --target gemini
|
|
76
|
+
|
|
77
|
+
# Google Antigravity IDE
|
|
78
|
+
npx dantelabs-agentic-school install gcp-openclaw --target antigravity
|
|
79
|
+
|
|
80
|
+
# OpenAI Codex CLI
|
|
81
|
+
npx dantelabs-agentic-school install gcp-openclaw --target codex
|
|
82
|
+
|
|
83
|
+
# OpenCode
|
|
84
|
+
npx dantelabs-agentic-school install gcp-openclaw --target opencode
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### 플랫폼별 호환성
|
|
88
|
+
|
|
89
|
+
| 플랫폼 | 디렉토리 | Skills | Agents | Commands |
|
|
90
|
+
| --- | --- | --- | --- | --- |
|
|
91
|
+
| `claude` (기본) | `.claude/` | ✅ | ✅ | ✅ |
|
|
92
|
+
| `gemini` | `.gemini/` | ✅ | ✅ | ❌ skip |
|
|
93
|
+
| `antigravity` | `.agent/` | ✅ | ❌ skip | ❌ skip |
|
|
94
|
+
| `codex` | `.agents/` | ✅ | ❌ skip | ❌ skip |
|
|
95
|
+
| `opencode` | `.opencode/` | ✅ | ✅ | ✅ |
|
|
96
|
+
| `agents` | `.agents/` | ✅ | ❌ skip | ❌ skip |
|
|
97
|
+
|
|
98
|
+
> **Note**: 모든 플러그인의 Skills는 모든 플랫폼에서 설치됩니다. Agents/Commands는 해당 플랫폼이 지원하지 않으면 자동으로 skip됩니다.
|
|
99
|
+
|
|
64
100
|
### 샘플 다운로드
|
|
65
101
|
|
|
66
102
|
학습용 샘플 파일을 다운로드하여 마케팅 자동화 파이프라인을 직접 체험해볼 수 있습니다.
|
|
@@ -309,10 +345,10 @@ python ~/.claude/skills/kie-video-generator/scripts/generate_video.py --credits
|
|
|
309
345
|
|
|
310
346
|
| 항목 | 개수 |
|
|
311
347
|
| --- | --- |
|
|
312
|
-
| 플러그인 |
|
|
348
|
+
| 플러그인 | 19개 (common 1 + marketing 8 + data-science 9 + gcp 1) |
|
|
313
349
|
| 에이전트 | 15개 |
|
|
314
|
-
| 커맨드 |
|
|
315
|
-
| 스킬 |
|
|
350
|
+
| 커맨드 | 20개 |
|
|
351
|
+
| 스킬 | 29개 (common 6 + marketing 13 + data-science 9 + gcp 1) |
|
|
316
352
|
|
|
317
353
|
## 플러그인 목록
|
|
318
354
|
|
|
@@ -653,6 +689,49 @@ projects/{project-name}/
|
|
|
653
689
|
|
|
654
690
|
---
|
|
655
691
|
|
|
692
|
+
## ☁️ GCP Plugins
|
|
693
|
+
|
|
694
|
+
Google Cloud Platform 배포 가이드 및 자동화 도구를 제공합니다.
|
|
695
|
+
|
|
696
|
+
### gcp-openclaw
|
|
697
|
+
|
|
698
|
+
GCP VM 인스턴스에 OpenClaw를 배포하는 포괄적인 가이드입니다.
|
|
699
|
+
|
|
700
|
+
| 컴포넌트 | 이름 | 설명 |
|
|
701
|
+
| --- | --- | --- |
|
|
702
|
+
| Command | /deploy-openclaw | GCP VM 생성 및 OpenClaw 배포 가이드 실행 |
|
|
703
|
+
| Skill | gcp-openclaw | GCP 배포 전체 워크플로 (VM, SSH, OpenClaw, GOG CLI) |
|
|
704
|
+
|
|
705
|
+
#### 사용 예시
|
|
706
|
+
|
|
707
|
+
```bash
|
|
708
|
+
# 기본 설정으로 배포 가이드 실행
|
|
709
|
+
/deploy-openclaw
|
|
710
|
+
|
|
711
|
+
# 서울 리전에 커스텀 설정으로 배포
|
|
712
|
+
/deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name openclaw-kr
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
#### 배포 파이프라인
|
|
716
|
+
|
|
717
|
+
```text
|
|
718
|
+
1. GCP 인증 및 프로젝트 설정
|
|
719
|
+
↓
|
|
720
|
+
2. VM 인스턴스 생성 (Ubuntu 22.04 LTS)
|
|
721
|
+
↓
|
|
722
|
+
3. SSH 구성 및 접속
|
|
723
|
+
↓
|
|
724
|
+
4. 의존성 설치 (Node.js v22+, Go 1.23+)
|
|
725
|
+
↓
|
|
726
|
+
5. OpenClaw 설치 및 초기화
|
|
727
|
+
↓
|
|
728
|
+
6. GOG CLI 설치 및 인증
|
|
729
|
+
↓
|
|
730
|
+
7. SSH 터널링으로 대시보드 접속
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
656
735
|
## 🔜 Coming Soon
|
|
657
736
|
|
|
658
737
|
### 💼 Business Ops Plugins (예정)
|
|
@@ -7,6 +7,7 @@ import { mkdir } from 'fs/promises';
|
|
|
7
7
|
|
|
8
8
|
import { getMarketplaceConfig, getPluginDependencies } from '../lib/config.js';
|
|
9
9
|
import { installPlugin } from '../lib/installer.js';
|
|
10
|
+
import { getPlatformConfig, PLATFORM_NAMES, DEFAULT_PLATFORM } from '../lib/platforms.js';
|
|
10
11
|
import logger from '../utils/logger.js';
|
|
11
12
|
import { resolvePath } from '../utils/fs-utils.js';
|
|
12
13
|
import { t } from '../i18n/index.js';
|
|
@@ -17,6 +18,7 @@ export default function installCommand(program) {
|
|
|
17
18
|
.alias('i')
|
|
18
19
|
.description(t('install.description'))
|
|
19
20
|
.option('-p, --path <path>', t('install.optionPath'))
|
|
21
|
+
.option('-t, --target <platform>', t('install.optionTarget'), DEFAULT_PLATFORM)
|
|
20
22
|
.option('-f, --force', t('install.optionForce'))
|
|
21
23
|
.option('--all', t('install.optionAll'))
|
|
22
24
|
.option('--no-common', t('install.optionNoCommon'))
|
|
@@ -25,13 +27,36 @@ export default function installCommand(program) {
|
|
|
25
27
|
const spinner = ora();
|
|
26
28
|
|
|
27
29
|
try {
|
|
30
|
+
// Validate platform
|
|
31
|
+
const platform = options.target;
|
|
32
|
+
if (!PLATFORM_NAMES.includes(platform)) {
|
|
33
|
+
logger.error(
|
|
34
|
+
t('install.invalidPlatform', {
|
|
35
|
+
name: platform,
|
|
36
|
+
valid: PLATFORM_NAMES.join(', ')
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const platformConfig = getPlatformConfig(platform);
|
|
43
|
+
|
|
28
44
|
// Determine installation path
|
|
29
45
|
const targetPath = options.path
|
|
30
46
|
? resolvePath(options.path)
|
|
31
47
|
: process.cwd();
|
|
32
|
-
const
|
|
48
|
+
const baseDir = join(targetPath, platformConfig.dir);
|
|
33
49
|
|
|
34
|
-
|
|
50
|
+
// Show platform info for non-default platforms
|
|
51
|
+
if (platform !== DEFAULT_PLATFORM) {
|
|
52
|
+
logger.info(
|
|
53
|
+
t('install.platformInfo', {
|
|
54
|
+
name: platformConfig.name,
|
|
55
|
+
description: platformConfig.description
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
logger.info(`${t('install.installTarget')}: ${chalk.cyan(baseDir)}`);
|
|
35
60
|
|
|
36
61
|
// Load marketplace config
|
|
37
62
|
spinner.start(t('install.loadingRegistry'));
|
|
@@ -100,18 +125,34 @@ export default function installCommand(program) {
|
|
|
100
125
|
const components = plugin.components || {};
|
|
101
126
|
|
|
102
127
|
if (components.agents?.length) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
if (platformConfig.agents) {
|
|
129
|
+
console.log(
|
|
130
|
+
chalk.gray(
|
|
131
|
+
` ${t('common.agents')}: ${components.agents.join(', ')}`
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
console.log(
|
|
136
|
+
chalk.yellow(
|
|
137
|
+
` ${t('common.agents')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.agents') })}`
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
108
141
|
}
|
|
109
142
|
if (components.commands?.length) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
if (platformConfig.commands) {
|
|
144
|
+
console.log(
|
|
145
|
+
chalk.gray(
|
|
146
|
+
` ${t('common.commands')}: /${components.commands.join(', /')}`
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
console.log(
|
|
151
|
+
chalk.yellow(
|
|
152
|
+
` ${t('common.commands')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.commands') })}`
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
115
156
|
}
|
|
116
157
|
if (components.skills?.length) {
|
|
117
158
|
console.log(
|
|
@@ -127,22 +168,25 @@ export default function installCommand(program) {
|
|
|
127
168
|
return;
|
|
128
169
|
}
|
|
129
170
|
|
|
130
|
-
// Create
|
|
131
|
-
if (!existsSync(
|
|
132
|
-
await mkdir(
|
|
171
|
+
// Create base directory
|
|
172
|
+
if (!existsSync(baseDir)) {
|
|
173
|
+
await mkdir(baseDir, { recursive: true });
|
|
133
174
|
}
|
|
134
175
|
|
|
135
176
|
// Install each plugin
|
|
136
177
|
let totalAgents = 0;
|
|
137
178
|
let totalCommands = 0;
|
|
138
179
|
let totalSkills = 0;
|
|
180
|
+
let totalSkippedAgents = 0;
|
|
181
|
+
let totalSkippedCommands = 0;
|
|
139
182
|
|
|
140
183
|
for (const plugin of pluginsToInstall) {
|
|
141
184
|
spinner.start(t('install.installing', { name: chalk.cyan(plugin.name) }));
|
|
142
185
|
|
|
143
186
|
try {
|
|
144
|
-
const results = await installPlugin(plugin,
|
|
187
|
+
const results = await installPlugin(plugin, baseDir, {
|
|
145
188
|
force: options.force,
|
|
189
|
+
platform,
|
|
146
190
|
onProgress: (type, name) => {
|
|
147
191
|
spinner.text = t('install.installingComponent', {
|
|
148
192
|
plugin: chalk.cyan(plugin.name),
|
|
@@ -155,6 +199,8 @@ export default function installCommand(program) {
|
|
|
155
199
|
totalAgents += results.agents;
|
|
156
200
|
totalCommands += results.commands;
|
|
157
201
|
totalSkills += results.skills;
|
|
202
|
+
totalSkippedAgents += results.skippedAgents;
|
|
203
|
+
totalSkippedCommands += results.skippedCommands;
|
|
158
204
|
|
|
159
205
|
spinner.succeed(t('install.installed', { name: chalk.cyan(plugin.name) }));
|
|
160
206
|
} catch (err) {
|
|
@@ -184,21 +230,46 @@ export default function installCommand(program) {
|
|
|
184
230
|
})}`
|
|
185
231
|
)
|
|
186
232
|
);
|
|
233
|
+
|
|
234
|
+
// Show skipped components warning
|
|
235
|
+
if (totalSkippedAgents > 0 || totalSkippedCommands > 0) {
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.yellow(
|
|
238
|
+
` ${t('install.skippedComponents', {
|
|
239
|
+
agents: totalSkippedAgents,
|
|
240
|
+
commands: totalSkippedCommands,
|
|
241
|
+
platform: platformConfig.name
|
|
242
|
+
})}`
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
187
247
|
console.log();
|
|
188
|
-
console.log(`${t('common.location')}: ${chalk.cyan(
|
|
248
|
+
console.log(`${t('common.location')}: ${chalk.cyan(baseDir)}`);
|
|
189
249
|
|
|
190
250
|
// Show next steps
|
|
191
251
|
console.log();
|
|
192
252
|
console.log(chalk.bold(`${t('install.nextSteps')}:`));
|
|
253
|
+
|
|
254
|
+
// Platform-specific next step hint
|
|
255
|
+
const cliCommands = {
|
|
256
|
+
claude: 'claude --help',
|
|
257
|
+
gemini: 'gemini',
|
|
258
|
+
antigravity: 'antigravity',
|
|
259
|
+
codex: 'codex --help',
|
|
260
|
+
opencode: 'opencode --help',
|
|
261
|
+
agents: 'claude --help',
|
|
262
|
+
};
|
|
263
|
+
const hintCmd = cliCommands[platform] || 'claude --help';
|
|
193
264
|
console.log(
|
|
194
|
-
` 1. ${t('install.nextStep1', { command: chalk.cyan(
|
|
265
|
+
` 1. ${t('install.nextStep1', { command: chalk.cyan(hintCmd) })}`
|
|
195
266
|
);
|
|
196
267
|
|
|
197
268
|
// Show example command based on installed plugins
|
|
198
269
|
const hasAnalyzeBrand = pluginsToInstall.some(
|
|
199
270
|
(p) => p.components?.commands?.includes('analyze-brand')
|
|
200
271
|
);
|
|
201
|
-
if (hasAnalyzeBrand) {
|
|
272
|
+
if (hasAnalyzeBrand && platformConfig.commands) {
|
|
202
273
|
console.log(
|
|
203
274
|
` 2. ${t('install.nextStep2', {
|
|
204
275
|
command: chalk.cyan('/analyze-brand --brand-doc ./your-brand.md')
|
|
@@ -6,6 +6,7 @@ import { existsSync } from 'fs';
|
|
|
6
6
|
|
|
7
7
|
import { getMarketplaceConfig } from '../lib/config.js';
|
|
8
8
|
import { uninstallPlugin } from '../lib/installer.js';
|
|
9
|
+
import { getPlatformConfig, PLATFORM_NAMES, DEFAULT_PLATFORM } from '../lib/platforms.js';
|
|
9
10
|
import logger from '../utils/logger.js';
|
|
10
11
|
import { resolvePath } from '../utils/fs-utils.js';
|
|
11
12
|
import { t } from '../i18n/index.js';
|
|
@@ -16,20 +17,37 @@ export default function uninstallCommand(program) {
|
|
|
16
17
|
.alias('rm')
|
|
17
18
|
.description(t('uninstall.description'))
|
|
18
19
|
.option('-p, --path <path>', t('uninstall.optionPath'))
|
|
20
|
+
.option('-t, --target <platform>', t('uninstall.optionTarget'), DEFAULT_PLATFORM)
|
|
19
21
|
.option('-y, --yes', t('uninstall.optionYes'))
|
|
20
22
|
.action(async (pluginName, options) => {
|
|
21
23
|
const spinner = ora();
|
|
22
24
|
|
|
23
25
|
try {
|
|
26
|
+
// Validate platform
|
|
27
|
+
const platform = options.target;
|
|
28
|
+
if (!PLATFORM_NAMES.includes(platform)) {
|
|
29
|
+
logger.error(
|
|
30
|
+
t('install.invalidPlatform', {
|
|
31
|
+
name: platform,
|
|
32
|
+
valid: PLATFORM_NAMES.join(', ')
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const platformConfig = getPlatformConfig(platform);
|
|
39
|
+
|
|
24
40
|
// Determine path
|
|
25
41
|
const targetPath = options.path
|
|
26
42
|
? resolvePath(options.path)
|
|
27
43
|
: process.cwd();
|
|
28
|
-
const
|
|
44
|
+
const baseDir = join(targetPath, platformConfig.dir);
|
|
29
45
|
|
|
30
|
-
// Check if
|
|
31
|
-
if (!existsSync(
|
|
32
|
-
logger.error(
|
|
46
|
+
// Check if target directory exists
|
|
47
|
+
if (!existsSync(baseDir)) {
|
|
48
|
+
logger.error(
|
|
49
|
+
t('uninstall.noTargetDir', { dir: platformConfig.dir, path: targetPath })
|
|
50
|
+
);
|
|
33
51
|
process.exit(1);
|
|
34
52
|
}
|
|
35
53
|
|
|
@@ -59,14 +77,30 @@ export default function uninstallCommand(program) {
|
|
|
59
77
|
const components = plugin.components || {};
|
|
60
78
|
|
|
61
79
|
if (components.agents?.length) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
if (platformConfig.agents) {
|
|
81
|
+
console.log(
|
|
82
|
+
chalk.gray(` ${t('common.agents')}: ${components.agents.join(', ')}`)
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
console.log(
|
|
86
|
+
chalk.yellow(
|
|
87
|
+
` ${t('common.agents')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.agents') })}`
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
65
91
|
}
|
|
66
92
|
if (components.commands?.length) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
if (platformConfig.commands) {
|
|
94
|
+
console.log(
|
|
95
|
+
chalk.gray(` ${t('common.commands')}: /${components.commands.join(', /')}`)
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(
|
|
99
|
+
chalk.yellow(
|
|
100
|
+
` ${t('common.commands')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.commands') })}`
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
70
104
|
}
|
|
71
105
|
if (components.skills?.length) {
|
|
72
106
|
console.log(
|
|
@@ -95,7 +129,7 @@ export default function uninstallCommand(program) {
|
|
|
95
129
|
// Uninstall
|
|
96
130
|
spinner.start(t('uninstall.uninstalling', { name: chalk.cyan(plugin.name) }));
|
|
97
131
|
|
|
98
|
-
const results = await uninstallPlugin(plugin,
|
|
132
|
+
const results = await uninstallPlugin(plugin, baseDir, { platform });
|
|
99
133
|
|
|
100
134
|
spinner.succeed(t('uninstall.uninstalled', { name: chalk.cyan(plugin.name) }));
|
|
101
135
|
|
|
@@ -110,6 +144,19 @@ export default function uninstallCommand(program) {
|
|
|
110
144
|
})
|
|
111
145
|
)
|
|
112
146
|
);
|
|
147
|
+
|
|
148
|
+
// Show skipped info
|
|
149
|
+
if (results.skippedAgents > 0 || results.skippedCommands > 0) {
|
|
150
|
+
console.log(
|
|
151
|
+
chalk.yellow(
|
|
152
|
+
` ${t('install.skippedComponents', {
|
|
153
|
+
agents: results.skippedAgents,
|
|
154
|
+
commands: results.skippedCommands,
|
|
155
|
+
platform: platformConfig.name
|
|
156
|
+
})}`
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
113
160
|
} catch (error) {
|
|
114
161
|
spinner.fail();
|
|
115
162
|
logger.error(error.message);
|
|
@@ -38,6 +38,11 @@ export default {
|
|
|
38
38
|
optionAll: 'Install all available plugins',
|
|
39
39
|
optionNoCommon: 'Skip common utilities',
|
|
40
40
|
optionDryRun: 'Show what would be installed without making changes',
|
|
41
|
+
optionTarget: 'Target platform (claude, gemini, antigravity, codex, opencode, agents)',
|
|
42
|
+
invalidPlatform: "Invalid platform '{name}'. Valid platforms: {valid}",
|
|
43
|
+
platformNotSupported: '{platform} does not support {component}. Skipping.',
|
|
44
|
+
skippedComponents: 'Skipped: {agents} agents, {commands} commands (not supported by {platform})',
|
|
45
|
+
platformInfo: 'Platform: {name} ({description})',
|
|
41
46
|
installTarget: 'Installation target',
|
|
42
47
|
loadingRegistry: 'Loading plugin registry...',
|
|
43
48
|
registryLoaded: 'Plugin registry loaded',
|
|
@@ -85,6 +90,8 @@ export default {
|
|
|
85
90
|
description: 'Uninstall a plugin from your project',
|
|
86
91
|
optionPath: 'Project path (default: current directory)',
|
|
87
92
|
optionYes: 'Skip confirmation prompt',
|
|
93
|
+
optionTarget: 'Target platform (claude, gemini, antigravity, codex, opencode, agents)',
|
|
94
|
+
noTargetDir: 'No {dir} directory found at {path}',
|
|
88
95
|
noClaudeDir: 'No .claude directory found at {path}',
|
|
89
96
|
loadingRegistry: 'Loading plugin registry...',
|
|
90
97
|
pluginNotFound: "Plugin '{name}' not found in registry",
|
|
@@ -38,6 +38,11 @@ export default {
|
|
|
38
38
|
optionAll: '모든 플러그인 설치',
|
|
39
39
|
optionNoCommon: 'common 유틸리티 제외',
|
|
40
40
|
optionDryRun: '실제 설치 없이 미리보기',
|
|
41
|
+
optionTarget: '대상 플랫폼 (claude, gemini, antigravity, codex, opencode, agents)',
|
|
42
|
+
invalidPlatform: "잘못된 플랫폼 '{name}'. 사용 가능: {valid}",
|
|
43
|
+
platformNotSupported: '{platform}은(는) {component}을(를) 지원하지 않습니다. 건너뜁니다.',
|
|
44
|
+
skippedComponents: '건너뜀: 에이전트 {agents}개, 명령어 {commands}개 ({platform} 미지원)',
|
|
45
|
+
platformInfo: '플랫폼: {name} ({description})',
|
|
41
46
|
installTarget: '설치 대상 경로',
|
|
42
47
|
loadingRegistry: '플러그인 목록 불러오는 중...',
|
|
43
48
|
registryLoaded: '플러그인 목록 로드 완료',
|
|
@@ -85,6 +90,8 @@ export default {
|
|
|
85
90
|
description: '프로젝트에서 플러그인 제거',
|
|
86
91
|
optionPath: '프로젝트 경로 (기본: 현재 디렉토리)',
|
|
87
92
|
optionYes: '확인 프롬프트 건너뛰기',
|
|
93
|
+
optionTarget: '대상 플랫폼 (claude, gemini, antigravity, codex, opencode, agents)',
|
|
94
|
+
noTargetDir: '{path}에 {dir} 디렉토리가 없습니다',
|
|
88
95
|
noClaudeDir: '{path}에 .claude 디렉토리가 없습니다',
|
|
89
96
|
loadingRegistry: '플러그인 목록 불러오는 중...',
|
|
90
97
|
pluginNotFound: "플러그인 '{name}'이(가) 레지스트리에 없습니다",
|
package/cli/src/lib/installer.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join, dirname } from 'path';
|
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { fetchFile, downloadDirectory } from './downloader.js';
|
|
6
|
+
import { getPlatformConfig, DEFAULT_PLATFORM } from './platforms.js';
|
|
6
7
|
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = dirname(__filename);
|
|
@@ -143,9 +144,10 @@ function getSourcePath(plugin) {
|
|
|
143
144
|
* └── skills/{skill-name}/
|
|
144
145
|
*/
|
|
145
146
|
export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
146
|
-
const { force = false, onProgress } = options;
|
|
147
|
+
const { force = false, onProgress, platform = DEFAULT_PLATFORM } = options;
|
|
147
148
|
const pluginName = plugin.name;
|
|
148
149
|
const sourcePath = getSourcePath(plugin); // e.g., "plugins/brand-analytics"
|
|
150
|
+
const platformConfig = getPlatformConfig(platform);
|
|
149
151
|
|
|
150
152
|
// Determine if we should use local or remote source
|
|
151
153
|
const useLocalSource = hasLocalSource(sourcePath);
|
|
@@ -161,21 +163,29 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
|
161
163
|
}
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
// Create base directories
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
// Create base directories only for supported component types
|
|
167
|
+
if (platformConfig.agents) {
|
|
168
|
+
await ensureDir(join(claudeDir, platformConfig.agents));
|
|
169
|
+
}
|
|
170
|
+
if (platformConfig.commands) {
|
|
171
|
+
await ensureDir(join(claudeDir, platformConfig.commands));
|
|
172
|
+
}
|
|
173
|
+
await ensureDir(join(claudeDir, platformConfig.skills));
|
|
168
174
|
|
|
169
175
|
const results = {
|
|
170
176
|
plugin: pluginName,
|
|
171
177
|
agents: 0,
|
|
172
178
|
commands: 0,
|
|
173
|
-
skills: 0
|
|
179
|
+
skills: 0,
|
|
180
|
+
skippedAgents: 0,
|
|
181
|
+
skippedCommands: 0,
|
|
174
182
|
};
|
|
175
183
|
|
|
176
|
-
// Install agents
|
|
177
|
-
if (components.agents?.length) {
|
|
178
|
-
|
|
184
|
+
// Install agents (skip if platform does not support agents)
|
|
185
|
+
if (components.agents?.length && !platformConfig.agents) {
|
|
186
|
+
results.skippedAgents = components.agents.length;
|
|
187
|
+
} else if (components.agents?.length) {
|
|
188
|
+
const agentsTargetDir = join(claudeDir, platformConfig.agents, pluginName);
|
|
179
189
|
await ensureDir(agentsTargetDir);
|
|
180
190
|
|
|
181
191
|
for (const agentName of components.agents) {
|
|
@@ -207,9 +217,11 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
|
207
217
|
}
|
|
208
218
|
}
|
|
209
219
|
|
|
210
|
-
// Install commands
|
|
211
|
-
if (components.commands?.length) {
|
|
212
|
-
|
|
220
|
+
// Install commands (skip if platform does not support commands)
|
|
221
|
+
if (components.commands?.length && !platformConfig.commands) {
|
|
222
|
+
results.skippedCommands = components.commands.length;
|
|
223
|
+
} else if (components.commands?.length) {
|
|
224
|
+
const commandsTargetDir = join(claudeDir, platformConfig.commands, pluginName);
|
|
213
225
|
await ensureDir(commandsTargetDir);
|
|
214
226
|
|
|
215
227
|
for (const commandName of components.commands) {
|
|
@@ -244,7 +256,7 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
|
244
256
|
// Install skills (preserve full directory structure)
|
|
245
257
|
if (components.skills?.length) {
|
|
246
258
|
for (const skillName of components.skills) {
|
|
247
|
-
const skillTargetDir = join(claudeDir,
|
|
259
|
+
const skillTargetDir = join(claudeDir, platformConfig.skills, skillName);
|
|
248
260
|
|
|
249
261
|
if (!force && existsSync(skillTargetDir)) {
|
|
250
262
|
throw new Error(`Directory exists: ${skillTargetDir}. Use --force to overwrite.`);
|
|
@@ -282,9 +294,11 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
|
282
294
|
/**
|
|
283
295
|
* Uninstall a plugin from .claude directory
|
|
284
296
|
*/
|
|
285
|
-
export async function uninstallPlugin(plugin, claudeDir) {
|
|
297
|
+
export async function uninstallPlugin(plugin, claudeDir, options = {}) {
|
|
298
|
+
const { platform = DEFAULT_PLATFORM } = options;
|
|
286
299
|
const pluginName = plugin.name;
|
|
287
300
|
const sourcePath = getSourcePath(plugin);
|
|
301
|
+
const platformConfig = getPlatformConfig(platform);
|
|
288
302
|
|
|
289
303
|
// Discover components dynamically (or use provided components for backward compatibility)
|
|
290
304
|
let components = plugin.components;
|
|
@@ -303,31 +317,37 @@ export async function uninstallPlugin(plugin, claudeDir) {
|
|
|
303
317
|
plugin: pluginName,
|
|
304
318
|
agents: 0,
|
|
305
319
|
commands: 0,
|
|
306
|
-
skills: 0
|
|
320
|
+
skills: 0,
|
|
321
|
+
skippedAgents: 0,
|
|
322
|
+
skippedCommands: 0,
|
|
307
323
|
};
|
|
308
324
|
|
|
309
|
-
// Remove agents
|
|
310
|
-
if (components.agents?.length) {
|
|
311
|
-
const agentsDir = join(claudeDir,
|
|
325
|
+
// Remove agents (skip if platform does not support agents)
|
|
326
|
+
if (components.agents?.length && platformConfig.agents) {
|
|
327
|
+
const agentsDir = join(claudeDir, platformConfig.agents, pluginName);
|
|
312
328
|
if (existsSync(agentsDir)) {
|
|
313
329
|
await rm(agentsDir, { recursive: true });
|
|
314
330
|
results.agents = components.agents.length;
|
|
315
331
|
}
|
|
332
|
+
} else if (components.agents?.length) {
|
|
333
|
+
results.skippedAgents = components.agents.length;
|
|
316
334
|
}
|
|
317
335
|
|
|
318
|
-
// Remove commands
|
|
319
|
-
if (components.commands?.length) {
|
|
320
|
-
const commandsDir = join(claudeDir,
|
|
336
|
+
// Remove commands (skip if platform does not support commands)
|
|
337
|
+
if (components.commands?.length && platformConfig.commands) {
|
|
338
|
+
const commandsDir = join(claudeDir, platformConfig.commands, pluginName);
|
|
321
339
|
if (existsSync(commandsDir)) {
|
|
322
340
|
await rm(commandsDir, { recursive: true });
|
|
323
341
|
results.commands = components.commands.length;
|
|
324
342
|
}
|
|
343
|
+
} else if (components.commands?.length) {
|
|
344
|
+
results.skippedCommands = components.commands.length;
|
|
325
345
|
}
|
|
326
346
|
|
|
327
347
|
// Remove skills
|
|
328
348
|
if (components.skills?.length) {
|
|
329
349
|
for (const skillName of components.skills) {
|
|
330
|
-
const skillDir = join(claudeDir,
|
|
350
|
+
const skillDir = join(claudeDir, platformConfig.skills, skillName);
|
|
331
351
|
if (existsSync(skillDir)) {
|
|
332
352
|
await rm(skillDir, { recursive: true });
|
|
333
353
|
results.skills++;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform definitions for multi-platform skill installation.
|
|
3
|
+
*
|
|
4
|
+
* Each platform maps to a specific directory structure and
|
|
5
|
+
* declares which component types (skills, agents, commands) it supports.
|
|
6
|
+
* A `null` value means the platform does not support that component type.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const PLATFORMS = {
|
|
10
|
+
claude: {
|
|
11
|
+
name: 'Claude Code',
|
|
12
|
+
dir: '.claude',
|
|
13
|
+
skills: 'skills',
|
|
14
|
+
agents: 'agents',
|
|
15
|
+
commands: 'commands',
|
|
16
|
+
description: 'Anthropic Claude Code',
|
|
17
|
+
},
|
|
18
|
+
gemini: {
|
|
19
|
+
name: 'Gemini CLI',
|
|
20
|
+
dir: '.gemini',
|
|
21
|
+
skills: 'skills',
|
|
22
|
+
agents: 'agents',
|
|
23
|
+
commands: null,
|
|
24
|
+
description: 'Google Gemini CLI',
|
|
25
|
+
},
|
|
26
|
+
antigravity: {
|
|
27
|
+
name: 'Antigravity',
|
|
28
|
+
dir: '.agent',
|
|
29
|
+
skills: 'skills',
|
|
30
|
+
agents: null,
|
|
31
|
+
commands: null,
|
|
32
|
+
description: 'Google Antigravity IDE',
|
|
33
|
+
},
|
|
34
|
+
codex: {
|
|
35
|
+
name: 'Codex CLI',
|
|
36
|
+
dir: '.agents',
|
|
37
|
+
skills: 'skills',
|
|
38
|
+
agents: null,
|
|
39
|
+
commands: null,
|
|
40
|
+
description: 'OpenAI Codex CLI',
|
|
41
|
+
},
|
|
42
|
+
opencode: {
|
|
43
|
+
name: 'OpenCode',
|
|
44
|
+
dir: '.opencode',
|
|
45
|
+
skills: 'skills',
|
|
46
|
+
agents: 'agents',
|
|
47
|
+
commands: 'commands',
|
|
48
|
+
description: 'OpenCode AI',
|
|
49
|
+
},
|
|
50
|
+
agents: {
|
|
51
|
+
name: 'Universal (.agents)',
|
|
52
|
+
dir: '.agents',
|
|
53
|
+
skills: 'skills',
|
|
54
|
+
agents: null,
|
|
55
|
+
commands: null,
|
|
56
|
+
description: 'Cross-platform .agents standard',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Valid platform name array */
|
|
61
|
+
export const PLATFORM_NAMES = Object.keys(PLATFORMS);
|
|
62
|
+
|
|
63
|
+
/** Default platform */
|
|
64
|
+
export const DEFAULT_PLATFORM = 'claude';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get platform configuration by name.
|
|
68
|
+
* @param {string} name - Platform key (e.g. 'claude', 'gemini')
|
|
69
|
+
* @returns {object} Platform config object
|
|
70
|
+
* @throws {Error} If the platform name is invalid
|
|
71
|
+
*/
|
|
72
|
+
export function getPlatformConfig(name) {
|
|
73
|
+
const config = PLATFORMS[name];
|
|
74
|
+
if (!config) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Unknown platform '${name}'. Valid platforms: ${PLATFORM_NAMES.join(', ')}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return config;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Compute the base installation directory for a given platform.
|
|
84
|
+
* @param {string} targetPath - Project root or custom path
|
|
85
|
+
* @param {string} platform - Platform key
|
|
86
|
+
* @returns {string} Absolute path to the platform directory (e.g. /project/.gemini)
|
|
87
|
+
*/
|
|
88
|
+
export function getTargetDir(targetPath, platform) {
|
|
89
|
+
const config = getPlatformConfig(platform);
|
|
90
|
+
return `${targetPath}/${config.dir}`;
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-openclaw
|
|
3
|
+
description: GCP VM 인스턴스를 생성하고 OpenClaw + GOG CLI를 설치하는 단계별 가이드를 실행합니다.
|
|
4
|
+
arguments:
|
|
5
|
+
- name: zone
|
|
6
|
+
description: GCP 존 (예: us-central1-a, asia-northeast3-a)
|
|
7
|
+
required: false
|
|
8
|
+
default: "us-central1-a"
|
|
9
|
+
- name: machine-type
|
|
10
|
+
description: VM 머신 타입 (예: e2-small, e2-medium)
|
|
11
|
+
required: false
|
|
12
|
+
default: "e2-small"
|
|
13
|
+
- name: instance-name
|
|
14
|
+
description: VM 인스턴스 이름
|
|
15
|
+
required: false
|
|
16
|
+
default: "openclaw-instance"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# /deploy-openclaw
|
|
20
|
+
|
|
21
|
+
GCP VM 인스턴스에 OpenClaw를 배포하는 전체 과정을 안내합니다.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 기본 설정으로 배포 가이드 실행
|
|
27
|
+
/deploy-openclaw
|
|
28
|
+
|
|
29
|
+
# 커스텀 설정
|
|
30
|
+
/deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name my-openclaw
|
|
31
|
+
|
|
32
|
+
# 서울 리전에 배포
|
|
33
|
+
/deploy-openclaw --zone asia-northeast3-a --instance-name openclaw-seoul
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## What This Command Does
|
|
37
|
+
|
|
38
|
+
이 명령어는 `gcp-openclaw` 스킬의 배포 가이드를 기반으로 다음 단계를 순차적으로 안내합니다:
|
|
39
|
+
|
|
40
|
+
### 1. 전제 조건 확인
|
|
41
|
+
- Google Cloud CLI (`gcloud`) 설치 여부 확인
|
|
42
|
+
- 인증 및 프로젝트 설정 안내
|
|
43
|
+
|
|
44
|
+
### 2. VM 인스턴스 생성
|
|
45
|
+
- 지정된 zone, machine-type, instance-name으로 VM 생성
|
|
46
|
+
- Ubuntu 22.04 LTS, 25GB 부트 디스크
|
|
47
|
+
|
|
48
|
+
### 3. 서버 환경 설정
|
|
49
|
+
- Node.js v22+, Go 1.23+ 설치
|
|
50
|
+
- 기본 빌드 도구 설치
|
|
51
|
+
|
|
52
|
+
### 4. OpenClaw 설치 및 초기화
|
|
53
|
+
- OpenClaw 전역 설치
|
|
54
|
+
- `openclaw onboard` 초기 설정
|
|
55
|
+
|
|
56
|
+
### 5. GOG CLI 설치 및 인증
|
|
57
|
+
- GOG CLI 빌드 및 설치
|
|
58
|
+
- Keyring 백엔드 설정 (headless 모드)
|
|
59
|
+
- OAuth 인증 (client_secret.json)
|
|
60
|
+
|
|
61
|
+
### 6. 접속 설정
|
|
62
|
+
- SSH 터널링으로 대시보드 접속
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
### Example 1: 기본 배포 (미국 중부)
|
|
67
|
+
```bash
|
|
68
|
+
/deploy-openclaw
|
|
69
|
+
```
|
|
70
|
+
`us-central1-a` 존에 `e2-small` 인스턴스를 `openclaw-instance`라는 이름으로 생성합니다.
|
|
71
|
+
|
|
72
|
+
### Example 2: 서울 리전 배포
|
|
73
|
+
```bash
|
|
74
|
+
/deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name openclaw-kr
|
|
75
|
+
```
|
|
76
|
+
한국 사용자를 위해 서울 리전에 더 큰 머신 타입으로 배포합니다.
|
|
77
|
+
|
|
78
|
+
## Related Skills
|
|
79
|
+
|
|
80
|
+
- `gcp-openclaw`: 전체 배포 가이드 참조 문서
|
|
81
|
+
|
|
82
|
+
## Notes
|
|
83
|
+
|
|
84
|
+
- GCP 프로젝트에 결제가 활성화되어 있어야 합니다.
|
|
85
|
+
- `e2-small` (2 vCPU, 2GB RAM) 이상의 머신 타입을 권장합니다.
|
|
86
|
+
- SSH 터널링을 통해 대시보드에 접속할 수 있습니다.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gcp-openclaw
|
|
3
|
+
description: VM 설정, SSH 구성, OpenClaw 설치 및 GOG CLI 설정을 포함하여 Google Cloud Platform에 OpenClaw를 배포하는 포괄적인 가이드입니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GCP OpenClaw 배포 가이드
|
|
7
|
+
|
|
8
|
+
이 스킬은 Google Cloud Platform (GCP) VM 인스턴스에 OpenClaw를 배포하기 위한 단계별 워크플로를 제공합니다. 초기 GCP 설정부터 OpenClaw 및 GOG CLI 구성까지 모든 과정을 다룹니다.
|
|
9
|
+
|
|
10
|
+
## 1. 전제 조건 (로컬 머신)
|
|
11
|
+
|
|
12
|
+
로컬 컴퓨터에 Google Cloud CLI (`gcloud`)가 설치되어 있고 인증이 완료되었는지 확인하십시오.
|
|
13
|
+
|
|
14
|
+
### 1.1 Google Cloud 인증
|
|
15
|
+
```bash
|
|
16
|
+
gcloud auth login --no-launch-browser
|
|
17
|
+
```
|
|
18
|
+
출력된 URL을 브라우저에서 열어 인증하고, 확인 코드를 터미널에 붙여넣습니다.
|
|
19
|
+
|
|
20
|
+
### 1.2 프로젝트 및 결제 설정
|
|
21
|
+
```bash
|
|
22
|
+
# 프로젝트 ID 설정
|
|
23
|
+
gcloud config set project [YOUR_PROJECT_ID]
|
|
24
|
+
|
|
25
|
+
# 필요한 서비스 활성화
|
|
26
|
+
gcloud services enable compute.googleapis.com
|
|
27
|
+
gcloud services enable cloudresourcemanager.googleapis.com
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. VM 인스턴스 생성
|
|
31
|
+
|
|
32
|
+
충분한 리소스를 가진 VM 인스턴스를 생성합니다.
|
|
33
|
+
**권장 사양:** `e2-small` (2 vCPU, 2GB RAM) 또는 `e2-medium` (2 vCPU, 4GB RAM).
|
|
34
|
+
**운영체제:** Ubuntu 22.04 LTS
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
gcloud compute instances create openclaw-instance \
|
|
38
|
+
--zone=us-central1-a \
|
|
39
|
+
--machine-type=e2-small \
|
|
40
|
+
--image-family=ubuntu-2204-lts \
|
|
41
|
+
--image-project=ubuntu-os-cloud \
|
|
42
|
+
--boot-disk-size=25GB \
|
|
43
|
+
--boot-disk-type=pd-balanced \
|
|
44
|
+
--labels=env=dev,app=openclaw
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 3. SSH 구성
|
|
48
|
+
|
|
49
|
+
VM에 쉽게 접속할 수 있도록 SSH를 구성합니다.
|
|
50
|
+
|
|
51
|
+
### 3.1 키 생성 및 접속
|
|
52
|
+
```bash
|
|
53
|
+
# 키 자동 생성 및 접속
|
|
54
|
+
gcloud compute ssh openclaw-instance --zone=us-central1-a
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3.2 표준 SSH 별칭 (선택 사항이지만 권장됨)
|
|
58
|
+
로컬 `~/.ssh/config` 파일에 다음 내용을 추가합니다:
|
|
59
|
+
```
|
|
60
|
+
Host openclaw
|
|
61
|
+
HostName [EXTERNAL_IP]
|
|
62
|
+
User [YOUR_USERNAME]
|
|
63
|
+
IdentityFile ~/.ssh/google_compute_engine
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 4. 의존성 및 도구 설치 (원격 서버)
|
|
67
|
+
|
|
68
|
+
서버에 SSH로 접속하여 필요한 도구를 설치합니다.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# 서버 접속
|
|
72
|
+
gcloud compute ssh openclaw-instance --zone=us-central1-a
|
|
73
|
+
|
|
74
|
+
# 업데이트 및 기본 도구 설치
|
|
75
|
+
sudo apt-get update
|
|
76
|
+
sudo apt-get install -y git curl make build-essential
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 4.1 Node.js 설치 (v22 이상)
|
|
80
|
+
OpenClaw는 Node.js v22 이상이 필요합니다.
|
|
81
|
+
```bash
|
|
82
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
|
83
|
+
sudo apt-get install -y nodejs
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 4.2 Go 설치 (최신 버전 필수)
|
|
87
|
+
GOG CLI 빌드를 위해서는 **반드시 최신 버전의 Go (1.23 이상)**가 필요합니다. 운영체제 기본 패키지(예: `apt install golang`)는 버전이 낮아 빌드에 실패할 수 있으므로, 아래와 같이 수동 설치를 권장합니다.
|
|
88
|
+
```bash
|
|
89
|
+
wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz
|
|
90
|
+
sudo rm -rf /usr/local/go
|
|
91
|
+
sudo tar -C /usr/local -xzf go1.23.6.linux-amd64.tar.gz
|
|
92
|
+
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
|
|
93
|
+
source ~/.bashrc
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 5. OpenClaw 설치
|
|
97
|
+
|
|
98
|
+
npm을 사용하여 OpenClaw를 전역으로 설치합니다.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# OpenClaw 설치
|
|
102
|
+
curl -fsSL https://openclaw.ai/install.sh | bash
|
|
103
|
+
# 또는 수동 설치:
|
|
104
|
+
# sudo npm install -g openclaw@latest
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 5.1 OpenClaw 초기화
|
|
108
|
+
```bash
|
|
109
|
+
openclaw onboard
|
|
110
|
+
```
|
|
111
|
+
대화형 설정 마법사를 따라 에이전트를 구성합니다.
|
|
112
|
+
|
|
113
|
+
## 6. GOG CLI 설치
|
|
114
|
+
|
|
115
|
+
Google 서비스 CLI 도구(`gogcli`)를 설치합니다.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# 리포지토리 복제
|
|
119
|
+
git clone https://github.com/steipete/gogcli.git
|
|
120
|
+
cd gogcli
|
|
121
|
+
|
|
122
|
+
# 바이너리 빌드
|
|
123
|
+
make
|
|
124
|
+
|
|
125
|
+
# 전역 설치 (심볼릭 링크)
|
|
126
|
+
sudo ln -sf $(pwd)/bin/gog /usr/local/bin/gog
|
|
127
|
+
|
|
128
|
+
# 설치 확인
|
|
129
|
+
gog --help
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 6.2 보안 Keyring 구성 (중요)
|
|
133
|
+
대화형 비밀번호 입력 없이 `gogcli`를 사용하려면(headless 모드), Keyring 백엔드를 **파일(file)**로 설정해야 합니다.
|
|
134
|
+
|
|
135
|
+
1. **환경 변수 설정 (영구 적용)**
|
|
136
|
+
OpenClaw 편의 기능과 GOG CLI 보안 설정을 위해 `~/.bashrc` 파일 하단에 다음 내용을 추가합니다:
|
|
137
|
+
```bash
|
|
138
|
+
# OpenClaw 자동 완성
|
|
139
|
+
source "$HOME/.openclaw/completions/openclaw.bash"
|
|
140
|
+
|
|
141
|
+
# Go 언어 경로 (앞서 추가하지 않았다면)
|
|
142
|
+
export PATH=$PATH:/usr/local/go/bin
|
|
143
|
+
|
|
144
|
+
# GOG CLI 설정 (File Backend & Headless)
|
|
145
|
+
export GOG_KEYRING_BACKEND=file
|
|
146
|
+
export GOG_KEYRING_PASSWORD='your_secure_password' # 강력한 비밀번호로 변경하세요
|
|
147
|
+
export GOG_ACCOUNT='your_email@gmail.com' # 이메일 반복 입력을 피하기 위해 설정
|
|
148
|
+
```
|
|
149
|
+
변경 사항 적용:
|
|
150
|
+
```bash
|
|
151
|
+
source ~/.bashrc
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
2. **구성 확인**
|
|
155
|
+
설정이 올바르게 적용되었는지 확인합니다:
|
|
156
|
+
```bash
|
|
157
|
+
gog config list
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 7. 구성 및 인증
|
|
161
|
+
|
|
162
|
+
### 7.1 Client Secret 설정
|
|
163
|
+
Google Cloud Console에서 다운로드한 `client_secret.json` 파일을 서버로 업로드합니다.
|
|
164
|
+
**로컬 머신에서 실행:**
|
|
165
|
+
```bash
|
|
166
|
+
gcloud compute scp /local/path/to/client_secret.json openclaw-instance:~/client_secret.json --zone=us-central1-a
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 7.2 GOG CLI 인증 (Headless 모드)
|
|
170
|
+
**원격 서버에서 실행:**
|
|
171
|
+
|
|
172
|
+
원격 서버에서는 브라우저를 실행할 수 없으므로 수동 인증 방식을 사용해야 합니다.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# 업로드한 시크릿 파일을 사용하여 인증 (이메일 주소 지정 권장)
|
|
176
|
+
gog auth login [YOUR_EMAIL@gmail.com] --client-secret ~/client_secret.json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
1. 명령어를 실행하면 터미널에 **인증 URL**이 출력됩니다.
|
|
180
|
+
2. 해당 URL을 **로컬 컴퓨터의 브라우저**에 복사하여 붙여넣고 로그인합니다.
|
|
181
|
+
3. 화면에 표시된 **인증 코드**를 복사하여 터미널에 붙여넣습니다.
|
|
182
|
+
|
|
183
|
+
> **참고:** 만약 브라우저가 열리려고 시도하다 실패한다면 `--manual` 플래그를 추가해 보십시오 (버전에 따라 다를 수 있음).
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
### 7.3 텔레그램 봇 구성 (선택 사항)
|
|
187
|
+
텔레그램을 통해 에이전트를 제어하려면 다음 단계를 따르세요:
|
|
188
|
+
|
|
189
|
+
1. **봇 생성:**
|
|
190
|
+
- 텔레그램을 열고 **@BotFather**를 검색합니다.
|
|
191
|
+
- `/newbot`을 보내고 안내에 따라 진행하여 **HTTP API Token**을 발급받습니다.
|
|
192
|
+
|
|
193
|
+
2. **OpenClaw 구성:**
|
|
194
|
+
- 구성 마법사를 실행합니다:
|
|
195
|
+
```bash
|
|
196
|
+
openclaw configure --section telegram
|
|
197
|
+
```
|
|
198
|
+
- 프롬프트에 **HTTP API Token**을 붙여넣습니다.
|
|
199
|
+
- 텔레그램 통합을 활성화합니다.
|
|
200
|
+
|
|
201
|
+
## 8. 웹 액세스 (SSH 터널링)
|
|
202
|
+
|
|
203
|
+
로컬 머신에서 OpenClaw 대시보드에 안전하게 접속하려면 다음 명령어를 사용하십시오:
|
|
204
|
+
|
|
205
|
+
**로컬 머신에서 실행:**
|
|
206
|
+
```bash
|
|
207
|
+
# 원격 포트 18789를 로컬 포트 18790으로 포워딩
|
|
208
|
+
gcloud compute ssh openclaw-instance --zone=us-central1-a -- -L 18790:localhost:18789 -N -f
|
|
209
|
+
```
|
|
210
|
+
대시보드 접속 주소: http://localhost:18790
|
|
211
|
+
|
|
212
|
+
## 9. 문제 해결 팁
|
|
213
|
+
|
|
214
|
+
- **메모리 문제:** `npm install` 실패 시 인스턴스 사양이 최소 `e2-small` (2GB RAM) 이상인지 확인하십시오. 스왑(swap) 파일을 추가하는 것도 방법입니다.
|
|
215
|
+
- **Go 버전:** `gogcli` 빌드 실패 시 `go version`을 확인하십시오. 반드시 1.22 버전 이상이어야 합니다.
|
|
216
|
+
- **포트 충돌:** 로컬에서 18789 포트가 이미 사용 중이라면 다른 포트를 사용하십시오 (예: `-L 18790:localhost:18789`).
|