@sayren/store-sdk 0.36.0 → 0.37.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/src/client.ts CHANGED
@@ -280,6 +280,7 @@ import {
280
280
  type VerifyPaymentProviderRequest,
281
281
  verifyPaymentProviderRequestSchema,
282
282
  } from "./schemas/payment";
283
+ import { publicPlansSchema } from "./schemas/plans";
283
284
  import {
284
285
  customerPointsSchema,
285
286
  type PointAdjustmentRequest,
@@ -369,12 +370,14 @@ import {
369
370
  addStorefrontDomainRequestSchema,
370
371
  type ChangeStorefrontSlugRequest,
371
372
  type ChangeStorefrontTemplateRequest,
373
+ type ConnectStorefrontGitRequest,
372
374
  type CreateStorefrontEditPreviewLinkRequest,
373
375
  type CreateStorefrontPreviewLinkRequest,
374
376
  type CreateStorefrontSiteRequest,
375
377
  type CreateStorefrontVersionRequest,
376
378
  changeStorefrontSlugRequestSchema,
377
379
  changeStorefrontTemplateRequestSchema,
380
+ connectStorefrontGitRequestSchema,
378
381
  createStorefrontEditPreviewLinkRequestSchema,
379
382
  createStorefrontPreviewLinkRequestSchema,
380
383
  createStorefrontSiteRequestSchema,
@@ -383,6 +386,8 @@ import {
383
386
  type DisableStorefrontHostingRequest,
384
387
  deleteStorefrontFileRequestSchema,
385
388
  disableStorefrontHostingRequestSchema,
389
+ type LinkStorefrontGitInstallationRequest,
390
+ linkStorefrontGitInstallationRequestSchema,
386
391
  type MoveStorefrontFileRequest,
387
392
  moveStorefrontFileRequestSchema,
388
393
  type PublishStorefrontVersionRequest,
@@ -394,8 +399,10 @@ import {
394
399
  rollbackStorefrontRequestSchema,
395
400
  runStorefrontCheckRequestSchema,
396
401
  type SearchStorefrontFilesRequest,
402
+ type SetStorefrontDeployApprovalRequest,
397
403
  type SetStorefrontVisibilityRequest,
398
404
  searchStorefrontFilesRequestSchema,
405
+ setStorefrontDeployApprovalRequestSchema,
399
406
  setStorefrontVisibilityRequestSchema,
400
407
  storefrontBuildSchema,
401
408
  storefrontCheckSchema,
@@ -407,6 +414,9 @@ import {
407
414
  storefrontFileMoveResultSchema,
408
415
  storefrontFileSearchResultSchema,
409
416
  storefrontFileWriteResultSchema,
417
+ storefrontGitConnectStateSchema,
418
+ storefrontGitRepositorySchema,
419
+ storefrontGitStatusSchema,
410
420
  storefrontHostingSchema,
411
421
  storefrontPreviewLinkSchema,
412
422
  storefrontSiteDomainSchema,
@@ -430,7 +440,7 @@ import {
430
440
  type WriteStorefrontFilesRequest,
431
441
  writeStorefrontFilesRequestSchema,
432
442
  } from "./schemas/storefront-hosting";
433
- import { publicPlansSchema, storeSubscriptionSchema } from "./schemas/subscription";
443
+ import { storeSubscriptionSchema } from "./schemas/subscription";
434
444
  import {
435
445
  type UploadPurpose,
436
446
  type UploadRequest,
@@ -2327,6 +2337,43 @@ export function createStoreClient(options: StoreClientOptions) {
2327
2337
  storeHttp.request("PUT", "/storefront/site/source", storefrontSourceReplacedSchema, {
2328
2338
  body: replaceStorefrontSourceRequestSchema.parse(body),
2329
2339
  }),
2340
+ /** `PUT /v1/storefront/site/deploy-approval` — 승인 없이 배포(상점 플랫폼 OWNER·ADMIN 전용) */
2341
+ setDeployApproval: (body: SetStorefrontDeployApprovalRequest) =>
2342
+ storeHttp.request("PUT", "/storefront/site/deploy-approval", storefrontSiteSchema, {
2343
+ body: setStorefrontDeployApprovalRequestSchema.parse(body),
2344
+ }),
2345
+ /** `GET /v1/storefront/site/git` — Git 원천(발행 소스를 둔 저장소)과 상태(#138) */
2346
+ getGit: () => storeHttp.request("GET", "/storefront/site/git", storefrontGitStatusSchema),
2347
+ /** `POST /v1/storefront/site/git/installations/state` — GitHub 설치 화면에 싣는 1회용 state(상점 플랫폼 전용) */
2348
+ createGitConnectState: () =>
2349
+ storeHttp.request(
2350
+ "POST",
2351
+ "/storefront/site/git/installations/state",
2352
+ storefrontGitConnectStateSchema,
2353
+ ),
2354
+ /** `POST /v1/storefront/site/git/installations` — GitHub App 설치 확인(상점 플랫폼 전용) → 그 설치의 저장소 */
2355
+ linkGitInstallation: (body: LinkStorefrontGitInstallationRequest) =>
2356
+ storeHttp.request(
2357
+ "POST",
2358
+ "/storefront/site/git/installations",
2359
+ z.array(storefrontGitRepositorySchema),
2360
+ { body: linkStorefrontGitInstallationRequestSchema.parse(body) },
2361
+ ),
2362
+ /** `GET /v1/storefront/site/git/installations/{installationId}/repositories` — 확인한 설치의 저장소(상점 플랫폼 전용) */
2363
+ listGitRepositories: (installationId: number) =>
2364
+ storeHttp.request(
2365
+ "GET",
2366
+ `/storefront/site/git/installations/${encodeURIComponent(String(installationId))}/repositories`,
2367
+ z.array(storefrontGitRepositorySchema),
2368
+ ),
2369
+ /** `POST /v1/storefront/site/git/connect` — 셀러 GitHub 저장소 연결(상점 플랫폼 전용) */
2370
+ connectGit: (body: ConnectStorefrontGitRequest) =>
2371
+ storeHttp.request("POST", "/storefront/site/git/connect", storefrontGitStatusSchema, {
2372
+ body: connectStorefrontGitRequestSchema.parse(body),
2373
+ }),
2374
+ /** `POST /v1/storefront/site/git/disconnect` — 연결 해제(sayren 저장소로 되돌림, 상점 플랫폼 전용) */
2375
+ disconnectGit: () =>
2376
+ storeHttp.request("POST", "/storefront/site/git/disconnect", storefrontGitStatusSchema),
2330
2377
  },
2331
2378
 
2332
2379
  subscription: {
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ export * from "./schemas/market";
42
42
  export * from "./schemas/misc";
43
43
  export * from "./schemas/order";
44
44
  export * from "./schemas/payment";
45
+ export * from "./schemas/plans";
45
46
  export * from "./schemas/points";
46
47
  export * from "./schemas/product";
47
48
  export * from "./schemas/product-info";
@@ -0,0 +1,104 @@
1
+ import { z } from "zod";
2
+ import { responsePlanCodeSchema } from "./subscription";
3
+
4
+ /**
5
+ * 요금제 비교표 — `GET /v1/public/plans`(인증 없음). 요금제마다 가격과 기능 키 값을 준다. 값은 서버가 관리하고(사내 어드민 게시) 언제든
6
+ * 바뀔 수 있으니 코드에 요금제 수치를 두지 않는다. 상점 하나의 구독·사용량은 `subscription.ts`다
7
+ */
8
+
9
+ const limitOrContract = (description: string) =>
10
+ z.number().int().nullable().describe(`${description}. null이면 계약(엔터프라이즈)·한도 없음`);
11
+
12
+ /**
13
+ * 요금제 기능 키 값 — 요금제 비교표·상점 플랫폼 요금제 화면이 쓴다. 키는 서버 판정의 기능 키와 같다(추가만 한다). 상위 요금제는 하위 요금제
14
+ * 기능을 모두 포함한다
15
+ */
16
+ export const planEntitlementsSchema = z
17
+ .object({
18
+ "features.members": z
19
+ .boolean()
20
+ .describe("소유자 외 구성원(관리자·직원)의 초대와 상점 접근. false면 소유자만 쓴다"),
21
+ "features.admin_api": z
22
+ .boolean()
23
+ .describe(
24
+ "관리 API(개인 액세스 토큰·외부 앱 토큰 호출). 상점 플랫폼·CLI·MCP는 모든 요금제에서 쓴다",
25
+ ),
26
+ "features.analytics": z.boolean().describe("애널리틱스 화면·조회 API"),
27
+ "limits.ai_reads_monthly": limitOrContract("MCP 읽기 월 한도"),
28
+ "limits.ai_actions_monthly": limitOrContract(
29
+ "MCP 쓰기(AI 작업) 월 한도 — 주문 처리는 막지 않는다",
30
+ ),
31
+ "limits.custom_domains": limitOrContract("내 도메인 수"),
32
+ "limits.webhooks": limitOrContract("웹훅 구독 수(0이면 쓸 수 없다)"),
33
+ "limits.public_apps": limitOrContract("공개 앱(앱 스토어 게시 앱) 설치 수"),
34
+ "limits.private_apps": limitOrContract(
35
+ "비공개 앱(이 상점 전용 앱) 설치 수(0이면 설치할 수 없다)",
36
+ ),
37
+ "limits.ai_credits_monthly": limitOrContract(
38
+ "AI 상점 도우미 월 크레딧(1 크레딧 = 모델 원가 $0.01, 이월 없음)",
39
+ ),
40
+ "limits.storefront_preview_hours_monthly": limitOrContract("편집 환경 시간(월, 시간)"),
41
+ "limits.storefront_build_minutes_monthly": limitOrContract("빌드 시간(월, 분)"),
42
+ "limits.storefront_requests_monthly": limitOrContract(
43
+ "스토어프론트 월 트래픽(요청 수) — 넘어도 막지 않고 안내만 한다",
44
+ ),
45
+ "limits.admin_api_requests_monthly": limitOrContract(
46
+ "관리 API 월 요청 — 넘으면 속도만 낮춘다(주문 처리 제외)",
47
+ ),
48
+ "rate_limit.admin_api": z
49
+ .number()
50
+ .nullable()
51
+ .describe("관리 API 분당 속도 배수(기본 분당 600 × 배수)"),
52
+ "storefront.cpu_ms": z.number().int().describe("스토어프론트 사이트 요청당 CPU 상한(ms)"),
53
+ "storefront.subrequests": z
54
+ .number()
55
+ .int()
56
+ .describe("스토어프론트 사이트 요청당 하위 요청 상한"),
57
+ "storefront.rate_limit_per_minute": z
58
+ .number()
59
+ .int()
60
+ .describe("스토어프론트 남용 방지 요청 속도 상한(사이트당 분당)"),
61
+ "seats.included": limitOrContract(
62
+ "포함 구성원 석 — 구성원은 모든 요금제에서 무료·무제한이라 null",
63
+ ),
64
+ "retention.analytics_days": z.number().int().describe("애널리틱스 보관 일수"),
65
+ "retention.api_logs_days": limitOrContract("API 로그 보관 일수(0이면 API 로그 없음)"),
66
+ "retention.audit_days": limitOrContract("감사 로그 보관 일수"),
67
+ "support.tier": z
68
+ .union([z.enum(["COMMUNITY", "EMAIL", "EMAIL_PRIORITY", "DEDICATED", "SLA"]), z.string()])
69
+ .describe(
70
+ "지원 등급(`COMMUNITY`·`EMAIL`·`EMAIL_PRIORITY`·`DEDICATED`·`SLA`, 새 등급이 더해질 수 있다)",
71
+ ),
72
+ })
73
+ .describe("요금제 기능 키 값");
74
+ export type PlanEntitlementValues = z.infer<typeof planEntitlementsSchema>;
75
+
76
+ /** 공개 요금제 한 줄 — `GET /v1/public/plans` */
77
+ export const publicPlanSchema = z.object({
78
+ code: responsePlanCodeSchema,
79
+ name: z.string().describe("표시 이름(Free·Pro·프리미엄·엔터프라이즈)"),
80
+ version: z.number().int().describe("요금제 버전 — 값이 바뀌면 새 버전이 게시된다"),
81
+ sort: z.number().int(),
82
+ purchasable: z
83
+ .boolean()
84
+ .describe("셀프 결제로 고를 수 있는가(Free·엔터프라이즈는 아니다 — 엔터프라이즈는 계약)"),
85
+ prices: z.object({
86
+ currency: z.literal("KRW"),
87
+ monthly: z.number().int().nullable().describe("월 가격(공급가, 부가세 별도)"),
88
+ startingAt: z.boolean().describe("「부터」 가격(엔터프라이즈 계약)"),
89
+ vatIncluded: z
90
+ .object({
91
+ monthly: z.number().int().nullable(),
92
+ })
93
+ .describe("표시 가격(부가세 포함) — 요금제 비교표는 이 값을 보인다"),
94
+ }),
95
+ entitlements: planEntitlementsSchema,
96
+ });
97
+ export type PublicPlan = z.infer<typeof publicPlanSchema>;
98
+
99
+ /** 요금제 비교표 — `GET /v1/public/plans`(인증 없음, 캐시 가능). 값은 서버가 관리하고 언제든 바뀔 수 있다 */
100
+ export const publicPlansSchema = z.object({
101
+ vatPercent: z.number().int().describe("부가세율(%)"),
102
+ plans: z.array(publicPlanSchema).describe("공개 요금제의 지금 버전 — 정렬 순"),
103
+ });
104
+ export type PublicPlans = z.infer<typeof publicPlansSchema>;
@@ -46,7 +46,8 @@ import { RESERVED_STORE_CODES } from "./store";
46
46
  * - 502 `STOREFRONT_SANDBOX_UNAVAILABLE` — 편집 Sandbox 호출이 실패했다. 잠시 뒤 다시 한다
47
47
  *
48
48
  * 소스 내려받기·올리기(#60, `/v1/storefront/site/source/**`, `sayren` CLI):
49
- * - 409 `SOURCE_CHANGED` — 작업 트리가 내려받은 뒤(`expectedBaseRef`) 바뀌었다(상점 플랫폼·AI 편집). `error.details.currentRef`
49
+ * - 409 `SOURCE_CHANGED` — 작업 트리가 내려받은 뒤(`expectedBaseRef`) 바뀌었고 같은 파일을 서로 다르게 고쳤다(상점 플랫폼·AI 편집). 겹치지 않으면 서버가
50
+ * 3-way 병합한다(응답 `merged`). `error.details.currentRef`·`conflicts`(충돌 파일)
50
51
  * - 409 `EDIT_SESSION_OPEN` — 편집 환경이 열려 있다. `endEditSession: true`로 끄고 진행할 수 있다
51
52
  * - 409 `SOURCE_BLOBS_MISSING` — 올리지 않은 파일 내용이 있다(`error.details.hashes`). `POST source/blobs`로 올리고 다시 한다
52
53
  * - 409 `DEPENDENCY_REJECTED` — 쓸 수 없는 npm 의존성이다(`error.details.rejected: [{ name, version, reason, detail }]`)
@@ -272,7 +273,23 @@ export const storefrontVersionSchema = z.object({
272
273
  .nullable()
273
274
  .optional()
274
275
  .describe(
275
- "만든 경로 (`CONSOLE` 상점 플랫폼 · `AI` AI 에이전트(MCP) · `CLI` `sayren` CLI · `API` 관리 API · `SYSTEM` 자동). 늘어날 수 있다",
276
+ "만든 경로 (`CONSOLE` 상점 플랫폼 · `AI` AI 에이전트(MCP) · `CLI` `sayren` CLI · `API` 관리 API · `SYSTEM` 자동 · `GIT` 연결한 저장소 push). 늘어날 수 있다",
277
+ ),
278
+ gitRef: z
279
+ .string()
280
+ .nullable()
281
+ .optional()
282
+ .describe("연결한 저장소의 push로 만든 버전의 브랜치. 그 밖은 null"),
283
+ commitSha: z
284
+ .string()
285
+ .nullable()
286
+ .optional()
287
+ .describe("이 버전 소스를 담은 저장소 커밋(Git 원천 사이트). 저장소에 없는 버전은 null"),
288
+ mainChanged: z
289
+ .boolean()
290
+ .optional()
291
+ .describe(
292
+ "이 버전을 만든 뒤 운영 브랜치가 바뀌어 발행할 수 없다(발행하면 409 `STOREFRONT_MAIN_CHANGED`). 최신 운영 브랜치를 합친 버전을 만든다. 버전 목록에만 싣는다",
276
293
  ),
277
294
  createdAt: z.string().describe("만든 시각 (ISO 8601)"),
278
295
  previewable: z
@@ -362,6 +379,12 @@ export type StorefrontSiteSnapshot = z.infer<typeof storefrontSiteSnapshotSchema
362
379
 
363
380
  export const storefrontSiteSchema = z.object({
364
381
  siteId: z.string(),
382
+ deployWithoutApproval: z
383
+ .boolean()
384
+ .optional()
385
+ .describe(
386
+ "승인 없이 배포 — 켜져 있으면 AI 에이전트의 발행·되돌리기가 승인 없이 실행된다. 상점 플랫폼에서 OWNER·ADMIN만 바꾼다",
387
+ ),
365
388
  slug: z.string().describe("주소 슬러그"),
366
389
  hostname: z.string().describe("대표 호스트 이름"),
367
390
  url: z.string().describe("대표 주소 (`https://{hostname}`)"),
@@ -1475,7 +1498,7 @@ export const replaceStorefrontSourceRequestSchema = z.object({
1475
1498
  expectedBaseRef: storefrontSourceRefSchema
1476
1499
  .optional()
1477
1500
  .describe(
1478
- "내려받은 작업 트리. 지금 작업 트리가 이 값일 때만 바꾼다. 생략하면 조건 없이 덮어쓴다",
1501
+ "내려받은 작업 트리. 그 뒤 작업 트리가 바뀌었으면 이 트리를 기준으로 3-way 병합하고, 같은 곳을 고쳤으면 409 `SOURCE_CHANGED`다. 생략하면 조건 없이 덮어쓴다",
1479
1502
  ),
1480
1503
  files: z
1481
1504
  .array(
@@ -1510,15 +1533,135 @@ export const replaceStorefrontSourceRequestSchema = z.object({
1510
1533
  export type ReplaceStorefrontSourceRequest = z.infer<typeof replaceStorefrontSourceRequestSchema>;
1511
1534
 
1512
1535
  export const storefrontSourceReplacedSchema = z.object({
1513
- sourceRef: storefrontSourceRefSchema.describe("새 작업 트리 — 다음 올리기의 `expectedBaseRef`"),
1536
+ sourceRef: storefrontSourceRefSchema.describe(
1537
+ "새 작업 트리(버전 만들기의 `expectedSourceRef`). 다음 올리기의 기준은 `uploadedRef`다",
1538
+ ),
1514
1539
  previousRef: z.string().describe("바꾸기 전 작업 트리"),
1515
1540
  treeSha256: z.string(),
1516
1541
  files: z.number().int().min(0),
1517
1542
  bytes: z.number().int().min(0),
1518
1543
  changed: z.boolean().describe("내용이 바뀌었는가 (같은 트리를 다시 올리면 false)"),
1544
+ merged: z
1545
+ .boolean()
1546
+ .optional()
1547
+ .describe(
1548
+ "내려받은 뒤 바뀐 작업 트리와 3-way 병합해 결과가 올린 목록과 다른가(서버 쪽 변경이 함께 남는다). 같은 곳을 고쳤으면 409 `SOURCE_CHANGED`(`details.conflicts` 충돌 파일)",
1549
+ ),
1550
+ uploadedRef: storefrontSourceRefSchema
1551
+ .optional()
1552
+ .describe(
1553
+ "올린 목록 그대로의 트리 — 다음 올리기의 `expectedBaseRef`(클라이언트가 가진 트리). 병합하지 않았으면 `sourceRef`와 같다",
1554
+ ),
1519
1555
  endedEditSession: z.boolean().describe("열린 편집 환경을 끄고 진행했는가"),
1520
1556
  dependencyWarnings: z
1521
1557
  .array(storefrontDependencyFindingSchema)
1522
1558
  .describe("배포를 막지 않는 의존성 경고 (보안 권고 등)"),
1523
1559
  });
1524
1560
  export type StorefrontSourceReplaced = z.infer<typeof storefrontSourceReplacedSchema>;
1561
+
1562
+ // ─── Git 원천 (#138 G5) ───────────────────────────────────
1563
+
1564
+ /**
1565
+ * `GET /v1/storefront/site/git` — 사이트의 Git 원천. 발행한 소스는 GitHub 저장소 main에 있다(사내 저장소 또는 셀러가 연결한 저장소).
1566
+ * 연결 전이면 필드가 null이다
1567
+ */
1568
+ export const storefrontGitStatusSchema = z.object({
1569
+ provider: z.enum(["GITHUB"]).nullable().describe("Git 원천이 켜져 있으면 `GITHUB`, 아니면 null"),
1570
+ owner: z
1571
+ .enum(["SAYREN", "SELLER"])
1572
+ .nullable()
1573
+ .describe("`SAYREN` sayren이 관리하는 저장소 · `SELLER` 셀러가 연결한 저장소"),
1574
+ repository: z
1575
+ .string()
1576
+ .nullable()
1577
+ .describe("연결한 셀러 저장소(`owner/name`). sayren 저장소면 null"),
1578
+ productionBranch: z.string().nullable().describe("운영 배포 브랜치"),
1579
+ rootDirectory: z.string().nullable().describe("저장소 안 사이트 루트(빈 문자열 = 저장소 루트)"),
1580
+ connectedAt: z.string().nullable().describe("원천이 켜진 시각 (ISO 8601)"),
1581
+ headCommitSha: z.string().nullable().describe("운영 브랜치의 마지막 커밋"),
1582
+ mainChanged: z
1583
+ .boolean()
1584
+ .describe(
1585
+ "발행 전 작업이 지금 운영 브랜치 위가 아니다 — 발행하려면 최신 운영 브랜치를 합친 버전을 만든다(아니면 409 `STOREFRONT_MAIN_CHANGED`)",
1586
+ ),
1587
+ autoPromotePaused: z
1588
+ .boolean()
1589
+ .describe(
1590
+ "되돌린 상태 — 저장소 push는 빌드만 하고 운영을 바꾸지 않는다. 버전을 발행하면 다시 켜진다",
1591
+ ),
1592
+ publishPending: z.boolean().describe("발행한 버전을 운영 브랜치에 올리는 중"),
1593
+ publishErrorCode: z
1594
+ .string()
1595
+ .nullable()
1596
+ .describe(
1597
+ "마지막 발행을 운영 브랜치에 올리지 못한 이유(브랜치 보호 등). 운영과 저장소가 다를 수 있다",
1598
+ ),
1599
+ archivedWorkRef: z
1600
+ .string()
1601
+ .nullable()
1602
+ .describe("셀러 저장소를 연결할 때 보관한 발행 전 작업 트리(연결 전 편집). 없으면 null"),
1603
+ });
1604
+ export type StorefrontGitStatus = z.infer<typeof storefrontGitStatusSchema>;
1605
+
1606
+ export const storefrontGitRepositorySchema = z.object({
1607
+ repoId: z.number().int().describe("GitHub 저장소 id"),
1608
+ fullName: z.string().describe("`owner/name`"),
1609
+ defaultBranch: z.string(),
1610
+ });
1611
+ export type StorefrontGitRepository = z.infer<typeof storefrontGitRepositorySchema>;
1612
+
1613
+ /** `POST /v1/storefront/site/git/installations` — GitHub App 설치 화면에서 돌아온 값(`installation_id`·`code`) */
1614
+ export const linkStorefrontGitInstallationRequestSchema = z.object({
1615
+ installationId: z.number().int().positive(),
1616
+ code: z
1617
+ .string()
1618
+ .min(1)
1619
+ .max(200)
1620
+ .describe("설치 화면이 돌려준 GitHub 사용자 인증 코드(한 번만 쓴다)"),
1621
+ state: z
1622
+ .string()
1623
+ .min(1)
1624
+ .max(200)
1625
+ .describe(
1626
+ "설치 화면으로 보내기 전에 받은 연결 state(`POST …/git/installations/state`, 한 번만 쓴다)",
1627
+ ),
1628
+ });
1629
+
1630
+ /** `POST /v1/storefront/site/git/installations/state` — GitHub 설치 화면에 싣는 1회용 state(이 상점·사용자, 10분) */
1631
+ export const storefrontGitConnectStateSchema = z.object({
1632
+ state: z.string(),
1633
+ expiresAt: z.string(),
1634
+ });
1635
+ export type StorefrontGitConnectState = z.infer<typeof storefrontGitConnectStateSchema>;
1636
+ export type LinkStorefrontGitInstallationRequest = z.infer<
1637
+ typeof linkStorefrontGitInstallationRequestSchema
1638
+ >;
1639
+
1640
+ /** `POST /v1/storefront/site/git/connect` */
1641
+ export const connectStorefrontGitRequestSchema = z.object({
1642
+ installationId: z.number().int().positive(),
1643
+ repoId: z.number().int().positive(),
1644
+ productionBranch: z
1645
+ .string()
1646
+ .min(1)
1647
+ .max(200)
1648
+ .regex(/^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/)
1649
+ .optional()
1650
+ .describe("운영 배포 브랜치(생략하면 저장소 기본 브랜치)"),
1651
+ rootDirectory: z
1652
+ .string()
1653
+ .max(200)
1654
+ .regex(/^$|^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/)
1655
+ .refine((v) => !v.split("/").some((seg) => seg === "." || seg === ".."), "상대 경로만")
1656
+ .optional()
1657
+ .describe("저장소 안 사이트 루트(생략하면 저장소 루트)"),
1658
+ });
1659
+ export type ConnectStorefrontGitRequest = z.infer<typeof connectStorefrontGitRequestSchema>;
1660
+
1661
+ /** `PUT /v1/storefront/site/deploy-approval` — 승인 없이 배포(#138) */
1662
+ export const setStorefrontDeployApprovalRequestSchema = z.object({
1663
+ deployWithoutApproval: z.boolean(),
1664
+ });
1665
+ export type SetStorefrontDeployApprovalRequest = z.infer<
1666
+ typeof setStorefrontDeployApprovalRequestSchema
1667
+ >;
@@ -47,6 +47,9 @@ export const subscriptionLimitsSchema = z.object({
47
47
  storefrontPreviewHoursMonthly: limit("스토어프론트 편집 미리보기 월 시간"),
48
48
  storefrontBuildMinutesMonthly: limit("스토어프론트 빌드 월 분"),
49
49
  adminApiRequestsMonthly: limit("관리 API 요청 월 포함량(넘으면 속도만 낮춘다, 주문 처리 제외)"),
50
+ publicApps: limit("공개 앱(앱 스토어에 게시된 앱) 설치 수").optional(),
51
+ privateApps: limit("비공개 앱(이 상점 전용 앱) 설치 수(0이면 설치할 수 없다)").optional(),
52
+ aiCreditsMonthly: limit("AI 상점 도우미 월 크레딧").optional(),
50
53
  });
51
54
  export type SubscriptionLimits = z.infer<typeof subscriptionLimitsSchema>;
52
55
 
@@ -65,6 +68,8 @@ export const subscriptionUsageSchema = z.object({
65
68
  seats: z.number().int().describe("현재 석 수 — 구성원(OWNER 포함) + 대기 중 초대"),
66
69
  customDomains: z.number().int().describe("현재 내 도메인 수(해제한 도메인 제외)"),
67
70
  webhooks: z.number().int().describe("현재 웹훅 구독 수"),
71
+ publicApps: z.number().int().optional().describe("현재 설치한 공개 앱 수"),
72
+ privateApps: z.number().int().optional().describe("현재 설치한 비공개 앱 수"),
68
73
  });
69
74
  export type SubscriptionUsage = z.infer<typeof subscriptionUsageSchema>;
70
75
 
@@ -89,6 +94,10 @@ export const storeSubscriptionSchema = z.object({
89
94
  features: z.object({
90
95
  adminApi: z.boolean().describe("관리 API(개인 액세스 토큰·외부 앱 토큰 호출)를 쓸 수 있다"),
91
96
  analytics: z.boolean().describe("애널리틱스 조회를 쓸 수 있다"),
97
+ members: z
98
+ .boolean()
99
+ .optional()
100
+ .describe("소유자 외 구성원이 상점에 접근할 수 있다(false면 소유자만, 구성원 목록은 남는다)"),
92
101
  storeClone: z.boolean().describe("더 쓰지 않는다 — 상점 복제 기능을 없애 항상 false"),
93
102
  removeBadge: z.boolean().describe("더 쓰지 않는다 — 배지 키를 없애 항상 false"),
94
103
  }),
@@ -107,6 +116,31 @@ export const storeSubscriptionSchema = z.object({
107
116
  resetsAt: z.string().describe("한도가 초기화되는 시각(ISO 8601)"),
108
117
  })
109
118
  .describe("MCP 읽기 한도 요약"),
119
+ aiCredits: z
120
+ .object({
121
+ monthly: z.number().int().nullable().describe("요금제 월 크레딧. null이면 한도 없음"),
122
+ granted: z.number().int().describe("이번 기간에 쓸 수 있는 추가 지급 크레딧"),
123
+ used: z.number().int().describe("이번 기간 사용한 크레딧"),
124
+ monthlyUsed: z.number().int().optional().describe("이번 기간 월 크레딧에서 쓴 양"),
125
+ purchased: z
126
+ .number()
127
+ .int()
128
+ .optional()
129
+ .describe("남은 구매 크레딧 — 구매일부터 1년, 월 크레딧이 초기화돼도 남는다"),
130
+ complimentary: z.number().int().optional().describe("남은 무상 지급 크레딧(이벤트·보상)"),
131
+ remaining: z
132
+ .number()
133
+ .int()
134
+ .nullable()
135
+ .describe("남은 크레딧(월 + 구매 + 무상). null이면 한도 없음"),
136
+ resetsAt: z
137
+ .string()
138
+ .describe("월 크레딧이 초기화되는 시각(ISO 8601). 월 크레딧은 이월하지 않는다"),
139
+ })
140
+ .optional()
141
+ .describe(
142
+ "AI 상점 도우미 크레딧 — 1 크레딧은 모델 원가 $0.01. 남은 크레딧이 없으면 새 실행을 만들 수 없다",
143
+ ),
110
144
  salesFeePercent: z.literal(0).describe("판매 수수료(%). 모든 요금제 0이다"),
111
145
  });
112
146
  export type StoreSubscription = z.infer<typeof storeSubscriptionSchema>;
@@ -133,90 +167,3 @@ export const planErrorDetailsSchema = z
133
167
  })
134
168
  .loose();
135
169
  export type PlanErrorDetails = z.infer<typeof planErrorDetailsSchema>;
136
-
137
- const limitOrContract = (description: string) =>
138
- z.number().int().nullable().describe(`${description}. null이면 계약(엔터프라이즈)·한도 없음`);
139
-
140
- /**
141
- * 요금제 기능 키 값 — 요금제 비교표·상점 플랫폼 요금제 화면이 쓴다. 키는 서버 판정의 기능 키와 같다(추가만 한다). 상위 요금제는 하위 요금제
142
- * 기능을 모두 포함한다
143
- */
144
- export const planEntitlementsSchema = z
145
- .object({
146
- "features.admin_api": z
147
- .boolean()
148
- .describe(
149
- "관리 API(개인 액세스 토큰·외부 앱 토큰 호출). 상점 플랫폼·CLI·MCP는 모든 요금제에서 쓴다",
150
- ),
151
- "features.analytics": z.boolean().describe("애널리틱스 화면·조회 API"),
152
- "limits.ai_reads_monthly": limitOrContract("MCP 읽기 월 한도"),
153
- "limits.ai_actions_monthly": limitOrContract(
154
- "MCP 쓰기(AI 작업) 월 한도 — 주문 처리는 막지 않는다",
155
- ),
156
- "limits.custom_domains": limitOrContract("내 도메인 수"),
157
- "limits.webhooks": limitOrContract("웹훅 구독 수(0이면 쓸 수 없다)"),
158
- "limits.storefront_preview_hours_monthly": limitOrContract("편집 환경 시간(월, 시간)"),
159
- "limits.storefront_build_minutes_monthly": limitOrContract("빌드 시간(월, 분)"),
160
- "limits.storefront_requests_monthly": limitOrContract(
161
- "스토어프론트 월 트래픽(요청 수) — 넘어도 막지 않고 안내만 한다",
162
- ),
163
- "limits.admin_api_requests_monthly": limitOrContract(
164
- "관리 API 월 요청 — 넘으면 속도만 낮춘다(주문 처리 제외)",
165
- ),
166
- "rate_limit.admin_api": z
167
- .number()
168
- .nullable()
169
- .describe("관리 API 분당 속도 배수(기본 분당 600 × 배수)"),
170
- "storefront.cpu_ms": z.number().int().describe("스토어프론트 사이트 요청당 CPU 상한(ms)"),
171
- "storefront.subrequests": z
172
- .number()
173
- .int()
174
- .describe("스토어프론트 사이트 요청당 하위 요청 상한"),
175
- "storefront.rate_limit_per_minute": z
176
- .number()
177
- .int()
178
- .describe("스토어프론트 남용 방지 요청 속도 상한(사이트당 분당)"),
179
- "seats.included": limitOrContract(
180
- "포함 구성원 석 — 구성원은 모든 요금제에서 무료·무제한이라 null",
181
- ),
182
- "retention.analytics_days": z.number().int().describe("애널리틱스 보관 일수"),
183
- "retention.api_logs_days": limitOrContract("API 로그 보관 일수(0이면 API 로그 없음)"),
184
- "retention.audit_days": limitOrContract("감사 로그 보관 일수"),
185
- "support.tier": z
186
- .union([z.enum(["COMMUNITY", "EMAIL", "EMAIL_PRIORITY", "DEDICATED", "SLA"]), z.string()])
187
- .describe(
188
- "지원 등급(`COMMUNITY`·`EMAIL`·`EMAIL_PRIORITY`·`DEDICATED`·`SLA`, 새 등급이 더해질 수 있다)",
189
- ),
190
- })
191
- .describe("요금제 기능 키 값");
192
- export type PlanEntitlementValues = z.infer<typeof planEntitlementsSchema>;
193
-
194
- /** 공개 요금제 한 줄 — `GET /v1/public/plans` */
195
- export const publicPlanSchema = z.object({
196
- code: responsePlanCodeSchema,
197
- name: z.string().describe("표시 이름(Free·Pro·프리미엄·엔터프라이즈)"),
198
- version: z.number().int().describe("요금제 버전 — 값이 바뀌면 새 버전이 게시된다"),
199
- sort: z.number().int(),
200
- purchasable: z
201
- .boolean()
202
- .describe("셀프 결제로 고를 수 있는가(Free·엔터프라이즈는 아니다 — 엔터프라이즈는 계약)"),
203
- prices: z.object({
204
- currency: z.literal("KRW"),
205
- monthly: z.number().int().nullable().describe("월 가격(공급가, 부가세 별도)"),
206
- startingAt: z.boolean().describe("「부터」 가격(엔터프라이즈 계약)"),
207
- vatIncluded: z
208
- .object({
209
- monthly: z.number().int().nullable(),
210
- })
211
- .describe("표시 가격(부가세 포함) — 요금제 비교표는 이 값을 보인다"),
212
- }),
213
- entitlements: planEntitlementsSchema,
214
- });
215
- export type PublicPlan = z.infer<typeof publicPlanSchema>;
216
-
217
- /** 요금제 비교표 — `GET /v1/public/plans`(인증 없음, 캐시 가능). 값은 서버가 관리하고 언제든 바뀔 수 있다 */
218
- export const publicPlansSchema = z.object({
219
- vatPercent: z.number().int().describe("부가세율(%)"),
220
- plans: z.array(publicPlanSchema).describe("공개 요금제의 지금 버전 — 정렬 순"),
221
- });
222
- export type PublicPlans = z.infer<typeof publicPlansSchema>;