relayax-cli 0.2.29 → 0.2.34
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/dist/commands/install.js +5 -4
- package/dist/commands/ping.js +6 -3
- package/dist/commands/publish.js +47 -5
- package/dist/commands/update.js +3 -2
- package/dist/lib/api.d.ts +2 -2
- package/dist/lib/api.js +22 -19
- package/dist/lib/command-adapter.js +95 -13
- package/dist/lib/version-check.js +4 -1
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -181,18 +181,19 @@ function registerInstall(program) {
|
|
|
181
181
|
return count;
|
|
182
182
|
}
|
|
183
183
|
const fileCount = countFiles(teamDir);
|
|
184
|
-
// 6. Record in installed.json (space_slug 포함)
|
|
184
|
+
// 6. Record in installed.json (team_id, space_slug 포함)
|
|
185
185
|
const installed = (0, config_js_1.loadInstalled)();
|
|
186
186
|
installed[slug] = {
|
|
187
|
+
team_id: team.id,
|
|
187
188
|
version: team.version,
|
|
188
189
|
installed_at: new Date().toISOString(),
|
|
189
190
|
files: [teamDir],
|
|
190
191
|
...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
|
|
191
192
|
};
|
|
192
193
|
(0, config_js_1.saveInstalled)(installed);
|
|
193
|
-
// 7. Report install + usage ping (non-blocking)
|
|
194
|
-
await (0, api_js_1.reportInstall)(slug, team.version);
|
|
195
|
-
(0, api_js_1.sendUsagePing)(slug, team.version);
|
|
194
|
+
// 7. Report install + usage ping (non-blocking, team_id 기반)
|
|
195
|
+
await (0, api_js_1.reportInstall)(team.id, slug, team.version);
|
|
196
|
+
(0, api_js_1.sendUsagePing)(team.id, slug, team.version);
|
|
196
197
|
const result = {
|
|
197
198
|
status: 'ok',
|
|
198
199
|
team: team.name,
|
package/dist/commands/ping.js
CHANGED
|
@@ -25,13 +25,16 @@ function registerPing(program) {
|
|
|
25
25
|
});
|
|
26
26
|
slug = match ?? slugInput;
|
|
27
27
|
}
|
|
28
|
-
// Resolve version from installed registry
|
|
28
|
+
// Resolve version and team_id from installed registry
|
|
29
29
|
const local = (0, config_js_1.loadInstalled)();
|
|
30
30
|
const global = (0, config_js_1.loadGlobalInstalled)();
|
|
31
31
|
const entry = local[slug] ?? global[slug];
|
|
32
32
|
const version = entry?.version;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const teamId = entry?.team_id;
|
|
34
|
+
// Fire-and-forget ping (team_id 기반, 없으면 skip)
|
|
35
|
+
if (teamId) {
|
|
36
|
+
await (0, api_js_1.sendUsagePing)(teamId, slug, version);
|
|
37
|
+
}
|
|
35
38
|
if (!opts.quiet) {
|
|
36
39
|
console.log(`RELAY_READY: ${slug}`);
|
|
37
40
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -249,10 +249,13 @@ function resolveLongDescription(teamDir, yamlValue) {
|
|
|
249
249
|
async function createTarball(teamDir) {
|
|
250
250
|
const tmpFile = path_1.default.join(os_1.default.tmpdir(), `relay-publish-${Date.now()}.tar.gz`);
|
|
251
251
|
const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(teamDir, d)));
|
|
252
|
-
// Include
|
|
252
|
+
// Include root-level files if they exist
|
|
253
253
|
const entries = [...dirsToInclude];
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
const rootFiles = ['relay.yaml', 'GUIDE.html', 'SKILL.md'];
|
|
255
|
+
for (const file of rootFiles) {
|
|
256
|
+
if (fs_1.default.existsSync(path_1.default.join(teamDir, file))) {
|
|
257
|
+
entries.push(file);
|
|
258
|
+
}
|
|
256
259
|
}
|
|
257
260
|
await (0, tar_1.create)({
|
|
258
261
|
gzip: true,
|
|
@@ -435,6 +438,30 @@ function registerPublish(program) {
|
|
|
435
438
|
}));
|
|
436
439
|
process.exit(1);
|
|
437
440
|
}
|
|
441
|
+
// Version bump suggestion for republishing
|
|
442
|
+
if (isTTY) {
|
|
443
|
+
const { select: promptVersion } = await import('@inquirer/prompts');
|
|
444
|
+
const [major, minor, patch] = config.version.split('.').map(Number);
|
|
445
|
+
const bumpPatch = `${major}.${minor}.${patch + 1}`;
|
|
446
|
+
const bumpMinor = `${major}.${minor + 1}.0`;
|
|
447
|
+
const bumpMajor = `${major + 1}.0.0`;
|
|
448
|
+
const newVersion = await promptVersion({
|
|
449
|
+
message: `버전 (현재 v${config.version}):`,
|
|
450
|
+
choices: [
|
|
451
|
+
{ name: `v${bumpPatch} — patch (버그 수정)`, value: bumpPatch },
|
|
452
|
+
{ name: `v${bumpMinor} — minor (기능 추가)`, value: bumpMinor },
|
|
453
|
+
{ name: `v${bumpMajor} — major (큰 변경)`, value: bumpMajor },
|
|
454
|
+
{ name: `v${config.version} — 유지`, value: config.version },
|
|
455
|
+
],
|
|
456
|
+
});
|
|
457
|
+
if (newVersion !== config.version) {
|
|
458
|
+
config.version = newVersion;
|
|
459
|
+
const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
|
|
460
|
+
yamlData.version = newVersion;
|
|
461
|
+
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
462
|
+
console.error(` → relay.yaml에 version: ${newVersion} 저장됨\n`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
438
465
|
// Visibility validation: must be explicitly set
|
|
439
466
|
if (!config.visibility) {
|
|
440
467
|
if (isTTY) {
|
|
@@ -480,12 +507,27 @@ function registerPublish(program) {
|
|
|
480
507
|
process.exit(1);
|
|
481
508
|
}
|
|
482
509
|
}
|
|
483
|
-
// Confirm visibility before publish
|
|
510
|
+
// Confirm visibility before publish (재배포 시 변경 기회 제공)
|
|
484
511
|
if (isTTY) {
|
|
512
|
+
const { select: promptConfirmVis } = await import('@inquirer/prompts');
|
|
485
513
|
const visLabel = config.visibility === 'public'
|
|
486
514
|
? '\x1b[32m공개\x1b[0m (마켓플레이스에 누구나 검색·설치)'
|
|
487
515
|
: '\x1b[33m비공개\x1b[0m (Space 멤버만 접근)';
|
|
488
|
-
|
|
516
|
+
const visAction = await promptConfirmVis({
|
|
517
|
+
message: `공개 범위: ${config.visibility === 'public' ? '공개' : '비공개'} — 유지할까요?`,
|
|
518
|
+
choices: [
|
|
519
|
+
{ name: `유지 — ${visLabel}`, value: 'keep' },
|
|
520
|
+
{ name: '변경', value: 'change' },
|
|
521
|
+
],
|
|
522
|
+
});
|
|
523
|
+
if (visAction === 'change') {
|
|
524
|
+
config.visibility = config.visibility === 'public' ? 'private' : 'public';
|
|
525
|
+
const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
|
|
526
|
+
yamlData.visibility = config.visibility;
|
|
527
|
+
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
528
|
+
const newLabel = config.visibility === 'public' ? '공개' : '비공개';
|
|
529
|
+
console.error(` → relay.yaml에 visibility: ${config.visibility} 저장됨 (${newLabel})\n`);
|
|
530
|
+
}
|
|
489
531
|
}
|
|
490
532
|
// Profile hint
|
|
491
533
|
if (isTTY) {
|
package/dist/commands/update.js
CHANGED
|
@@ -67,6 +67,7 @@ function registerUpdate(program) {
|
|
|
67
67
|
const hadDeployedFiles = (currentEntry?.deployed_files?.length ?? 0) > 0;
|
|
68
68
|
// Update installed.json with new version
|
|
69
69
|
installed[slug] = {
|
|
70
|
+
team_id: team.id,
|
|
70
71
|
version: latestVersion,
|
|
71
72
|
installed_at: new Date().toISOString(),
|
|
72
73
|
files,
|
|
@@ -75,8 +76,8 @@ function registerUpdate(program) {
|
|
|
75
76
|
// Clear deployed_files — agent must re-deploy and call deploy-record
|
|
76
77
|
};
|
|
77
78
|
(0, config_js_1.saveInstalled)(installed);
|
|
78
|
-
// Report install (non-blocking)
|
|
79
|
-
await (0, api_js_1.reportInstall)(slug);
|
|
79
|
+
// Report install (non-blocking, team_id 기반)
|
|
80
|
+
await (0, api_js_1.reportInstall)(team.id, slug, latestVersion);
|
|
80
81
|
const result = {
|
|
81
82
|
status: 'updated',
|
|
82
83
|
slug,
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface TeamVersionInfo {
|
|
|
7
7
|
created_at: string;
|
|
8
8
|
}
|
|
9
9
|
export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
|
|
10
|
-
export declare function reportInstall(slug: string, version?: string): Promise<void>;
|
|
10
|
+
export declare function reportInstall(teamId: string, slug: string, version?: string): Promise<void>;
|
|
11
11
|
/**
|
|
12
12
|
* Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
|
|
13
13
|
* POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
|
|
@@ -19,5 +19,5 @@ export interface ResolvedSlug {
|
|
|
19
19
|
full: string;
|
|
20
20
|
}
|
|
21
21
|
export declare function resolveSlugFromServer(name: string): Promise<ResolvedSlug[]>;
|
|
22
|
-
export declare function sendUsagePing(slug: string, version?: string): Promise<void>;
|
|
22
|
+
export declare function sendUsagePing(teamId: string, slug: string, version?: string): Promise<void>;
|
|
23
23
|
export declare function followBuilder(username: string): Promise<void>;
|
package/dist/lib/api.js
CHANGED
|
@@ -42,26 +42,30 @@ async function fetchTeamVersions(slug) {
|
|
|
42
42
|
}
|
|
43
43
|
return res.json();
|
|
44
44
|
}
|
|
45
|
-
async function reportInstall(slug, version) {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
if (version) {
|
|
51
|
-
headers['Content-Type'] = 'application/json';
|
|
45
|
+
async function reportInstall(teamId, slug, version) {
|
|
46
|
+
const url = `${config_js_1.API_URL}/api/teams/${teamId}/install`;
|
|
47
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
48
|
+
const body = { slug };
|
|
49
|
+
if (version)
|
|
52
50
|
body.version = version;
|
|
53
|
-
}
|
|
54
51
|
const token = await (0, config_js_1.getValidToken)();
|
|
55
52
|
if (token) {
|
|
56
53
|
headers['Authorization'] = `Bearer ${token}`;
|
|
57
54
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(url, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers,
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const text = await res.text().catch(() => '');
|
|
63
|
+
console.error(`\x1b[33m⚠ 설치 카운트 업데이트 실패 (${res.status}): ${text}\x1b[0m`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// network error: ignore silently
|
|
68
|
+
}
|
|
65
69
|
}
|
|
66
70
|
/**
|
|
67
71
|
* Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
|
|
@@ -97,15 +101,14 @@ async function resolveSlugFromServer(name) {
|
|
|
97
101
|
const data = (await res.json());
|
|
98
102
|
return data.results;
|
|
99
103
|
}
|
|
100
|
-
async function sendUsagePing(slug, version) {
|
|
101
|
-
const registrySlug = slug.startsWith('@') ? slug.slice(1) : slug;
|
|
104
|
+
async function sendUsagePing(teamId, slug, version) {
|
|
102
105
|
const { createHash } = await import('crypto');
|
|
103
106
|
const { hostname, userInfo } = await import('os');
|
|
104
107
|
const deviceHash = createHash('sha256')
|
|
105
108
|
.update(`${hostname()}:${userInfo().username}`)
|
|
106
109
|
.digest('hex');
|
|
107
|
-
const url = `${config_js_1.API_URL}/api/
|
|
108
|
-
const payload = { device_hash: deviceHash };
|
|
110
|
+
const url = `${config_js_1.API_URL}/api/teams/${teamId}/ping`;
|
|
111
|
+
const payload = { device_hash: deviceHash, slug };
|
|
109
112
|
if (version)
|
|
110
113
|
payload.installed_version = version;
|
|
111
114
|
const headers = { 'Content-Type': 'application/json' };
|
|
@@ -183,11 +183,28 @@ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 이 단계를
|
|
|
183
183
|
- CLI가 init과 login을 자동으로 처리합니다 (사용자가 별도 실행할 필요 없음).
|
|
184
184
|
- JSON 출력에서 \`install_path\` (패키지 경로)를 확인합니다.
|
|
185
185
|
|
|
186
|
-
#### 2-2. 배치 범위 선택
|
|
186
|
+
#### 2-2. 배치 범위 선택 (추천 포함)
|
|
187
|
+
|
|
188
|
+
팀의 성격을 분석하여 글로벌/로컬 중 적합한 쪽을 추천합니다.
|
|
189
|
+
|
|
190
|
+
**추천 로직:**
|
|
191
|
+
- **글로벌 추천** — 범용 유틸리티 팀: 코드 리뷰, 문서 생성, 테스트, 번역 등 프로젝트에 무관하게 사용하는 도구성 팀
|
|
192
|
+
- **로컬 추천** — 프로젝트 특화 팀: 특정 프레임워크/스택 전용, 프로젝트별 워크플로우, 팀 내부 컨벤션에 의존하는 팀
|
|
193
|
+
|
|
194
|
+
판단 기준 (다운로드된 패키지의 relay.yaml과 콘텐츠를 분석):
|
|
195
|
+
1. \`tags\`에 특정 프레임워크/스택 키워드가 있으면 (nextjs, supabase, react 등) → 로컬 추천
|
|
196
|
+
2. \`requires\`에 프로젝트 종속 의존성이 있으면 (npm 패키지, 특정 파일 구조) → 로컬 추천
|
|
197
|
+
3. rules/ 파일이 있으면 (프로젝트 컨벤션 주입) → 로컬 추천
|
|
198
|
+
4. 범용 키워드 (utility, review, docs, testing 등) → 글로벌 추천
|
|
199
|
+
5. 판단이 어려우면 → 글로벌 추천 (기본값)
|
|
187
200
|
|
|
188
201
|
**AskUserQuestion 호출:**
|
|
189
|
-
- question: "
|
|
190
|
-
- options: \`["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만)"]\`
|
|
202
|
+
- question: "{추천 이유}. {추천}에 설치할까요?"
|
|
203
|
+
- options: \`["글로벌 (모든 프로젝트) ✓ 추천", "로컬 (이 프로젝트만)"]\` 또는 \`["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만) ✓ 추천"]\`
|
|
204
|
+
|
|
205
|
+
예시:
|
|
206
|
+
- "범용 코드 리뷰 도구입니다. 글로벌에 설치할까요?" → \`["글로벌 (모든 프로젝트) ✓ 추천", "로컬 (이 프로젝트만)"]\`
|
|
207
|
+
- "Next.js + Supabase 전용 팀입니다. 이 프로젝트에 설치할까요?" → \`["글로벌 (모든 프로젝트)", "로컬 (이 프로젝트만) ✓ 추천"]\`
|
|
191
208
|
|
|
192
209
|
**응답 처리:**
|
|
193
210
|
- "글로벌" → \`~/.claude/\`에 배치
|
|
@@ -206,14 +223,65 @@ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 이 단계를
|
|
|
206
223
|
relay deploy-record <slug> --scope <global|local> --files <배치된_파일1> <배치된_파일2> ...
|
|
207
224
|
\`\`\`
|
|
208
225
|
|
|
209
|
-
#### 2-5. Requirements
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
226
|
+
#### 2-5. Requirements 체크리스트 (필수 — 항목이 있으면 반드시 수행)
|
|
227
|
+
|
|
228
|
+
\`<install_path>/relay.yaml\`의 \`requires\` 섹션을 읽고, **각 항목을 하나씩 확인하여 체크리스트로 표시**합니다.
|
|
229
|
+
requires 섹션이 없거나 비어있으면 이 단계를 건너뜁니다.
|
|
230
|
+
|
|
231
|
+
**출력 형식** (반드시 이 형식으로 사용자에게 보여줍니다):
|
|
232
|
+
\`\`\`
|
|
233
|
+
📋 Requirements 확인
|
|
234
|
+
|
|
235
|
+
[runtime]
|
|
236
|
+
✅ Node.js >=18 — v20.11.0 확인됨
|
|
237
|
+
❌ Python >=3.10 — v3.8.5 (업그레이드 필요)
|
|
238
|
+
|
|
239
|
+
[cli]
|
|
240
|
+
✅ playwright — 설치됨
|
|
241
|
+
❌ ffmpeg — 미설치 → 설치 명령: brew install ffmpeg
|
|
242
|
+
|
|
243
|
+
[npm]
|
|
244
|
+
✅ sharp — 설치됨
|
|
245
|
+
❌ puppeteer — 미설치 → 설치 중...
|
|
246
|
+
|
|
247
|
+
[env]
|
|
248
|
+
✅ OPENAI_API_KEY — 설정됨
|
|
249
|
+
❌ SLACK_WEBHOOK_URL (선택) — 미설정. 알림 전송에 필요
|
|
250
|
+
|
|
251
|
+
[mcp]
|
|
252
|
+
⚙️ supabase — MCP 서버 설정 필요 (아래 안내 참고)
|
|
253
|
+
|
|
254
|
+
[teams]
|
|
255
|
+
✅ @alice/doc-writer — 이미 설치됨
|
|
256
|
+
📦 @bob/utils — 미설치 → 설치 중...
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
**처리 규칙 (각 카테고리별):**
|
|
260
|
+
|
|
261
|
+
1. **runtime**: \`node --version\`, \`python3 --version\`으로 확인. 버전 미달이면 ❌ 표시 후 업그레이드 안내.
|
|
262
|
+
2. **cli**: \`which <name>\`으로 확인.
|
|
263
|
+
- 설치됨 → ✅
|
|
264
|
+
- 미설치 + \`install\` 필드 있음 → 사용자에게 설치할지 물어본 후 실행
|
|
265
|
+
- 미설치 + \`install\` 필드 없음 → ❌ 표시 후 수동 설치 안내
|
|
266
|
+
3. **npm**: \`npm list <package> 2>/dev/null\`으로 확인.
|
|
267
|
+
- 설치됨 → ✅
|
|
268
|
+
- 미설치 + required → \`npm install <package>\` 실행
|
|
269
|
+
- 미설치 + optional → ❌ 표시 후 안내만
|
|
270
|
+
4. **env**: \`echo $<NAME>\`으로 확인.
|
|
271
|
+
- 설정됨 → ✅
|
|
272
|
+
- 미설정 + required → ❌ 표시 후 \`description\`과 함께 설정 방법 안내
|
|
273
|
+
- 미설정 + optional → ⚠️ 표시 후 용도 안내
|
|
274
|
+
5. **mcp**: MCP 서버 설정이 필요한 경우 ⚙️ 표시 후 설정 방법을 상세히 안내.
|
|
275
|
+
- \`config\` 필드가 있으면 settings.json에 추가할 JSON 블록을 보여줍니다.
|
|
276
|
+
- \`env\` 필드가 있으면 필요한 환경변수도 함께 안내합니다.
|
|
277
|
+
6. **teams**: \`relay list --json\`으로 설치 여부 확인.
|
|
278
|
+
- 설치됨 → ✅
|
|
279
|
+
- 미설치 → \`relay install <@author/team> --json\` 실행하여 재귀 설치
|
|
280
|
+
|
|
281
|
+
**중요**: 모든 required 항목이 ❌인 경우, 체크리스트 끝에 경고를 표시합니다:
|
|
282
|
+
\`\`\`
|
|
283
|
+
⚠️ 필수 요구사항이 충족되지 않았습니다. 팀 기능이 제한될 수 있습니다.
|
|
284
|
+
\`\`\`
|
|
217
285
|
${LOGIN_JIT_GUIDE}
|
|
218
286
|
|
|
219
287
|
### Step 3. 완료 & 팔로우 제안
|
|
@@ -401,7 +469,21 @@ ${LOGIN_JIT_GUIDE}
|
|
|
401
469
|
|
|
402
470
|
## 인터랙션 플로우
|
|
403
471
|
|
|
404
|
-
이 커맨드는
|
|
472
|
+
이 커맨드는 6단계 인터랙션으로 진행됩니다. 각 단계에서 반드시 AskUserQuestion 도구를 사용하세요.
|
|
473
|
+
|
|
474
|
+
### Step 0. 버전 범프
|
|
475
|
+
|
|
476
|
+
relay.yaml의 현재 \`version\`을 읽고 semver 범프를 제안합니다.
|
|
477
|
+
|
|
478
|
+
**AskUserQuestion 호출:**
|
|
479
|
+
- question: "버전을 올릴까요? (현재 v{version})"
|
|
480
|
+
- options: \`["v{patch} — patch (버그 수정)", "v{minor} — minor (기능 추가)", "v{major} — major (큰 변경)", "v{version} — 유지"]\`
|
|
481
|
+
|
|
482
|
+
예시: 현재 v1.0.0이면 → \`["v1.0.1 — patch", "v1.1.0 — minor", "v2.0.0 — major", "v1.0.0 — 유지"]\`
|
|
483
|
+
|
|
484
|
+
**응답 처리:**
|
|
485
|
+
- 유지 외 선택 → relay.yaml의 version을 선택된 값으로 업데이트
|
|
486
|
+
- 유지 → 그대로 진행
|
|
405
487
|
|
|
406
488
|
### Step 1. 공개 범위 선택
|
|
407
489
|
|
|
@@ -604,7 +686,7 @@ requires: env 2개, cli 1개
|
|
|
604
686
|
- options: \`["배포", "취소"]\`
|
|
605
687
|
|
|
606
688
|
**응답 처리:**
|
|
607
|
-
- "배포" → \`relay publish\` 실행
|
|
689
|
+
- "배포" → \`relay publish --json\` 실행 (슬래시 커맨드에서 이미 버전/visibility를 relay.yaml에 저장했으므로 --json으로 인터랙티브 프롬프트 생략)
|
|
608
690
|
- "취소" → 중단
|
|
609
691
|
|
|
610
692
|
#### 5-3. 배포 완료 & 온보딩 가이드
|
|
@@ -43,7 +43,10 @@ async function checkTeamVersion(slug, force) {
|
|
|
43
43
|
const team = await (0, api_js_1.fetchTeamInfo)(slug);
|
|
44
44
|
(0, update_cache_js_1.updateCacheTimestamp)(slug);
|
|
45
45
|
// Fire-and-forget usage ping (only when cache expired = actual API call happened)
|
|
46
|
-
|
|
46
|
+
const teamId = entry.team_id ?? team.id;
|
|
47
|
+
if (teamId) {
|
|
48
|
+
(0, api_js_1.sendUsagePing)(teamId, slug, entry.version);
|
|
49
|
+
}
|
|
47
50
|
if (team.version !== entry.version) {
|
|
48
51
|
return {
|
|
49
52
|
type: 'team',
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface ContactItem {
|
|
|
4
4
|
value: string;
|
|
5
5
|
}
|
|
6
6
|
export interface InstalledTeam {
|
|
7
|
+
/** team UUID — 설치 카운트/핑 등 서버 통신에 사용 */
|
|
8
|
+
team_id?: string;
|
|
7
9
|
version: string;
|
|
8
10
|
installed_at: string;
|
|
9
11
|
files: string[];
|
|
@@ -20,6 +22,8 @@ export interface InstalledRegistry {
|
|
|
20
22
|
[scopedSlug: string]: InstalledTeam;
|
|
21
23
|
}
|
|
22
24
|
export interface TeamRegistryInfo {
|
|
25
|
+
/** team UUID */
|
|
26
|
+
id: string;
|
|
23
27
|
/** scoped slug 포맷: "@owner/name" */
|
|
24
28
|
slug: string;
|
|
25
29
|
name: string;
|