sayren 0.15.1 → 0.16.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/dist/index.mjs +205 -540
- package/package.json +5 -5
- package/dist/boilerplate/.claude/skills/sayren/SKILL.md +0 -18
- package/dist/boilerplate/.mcp.json +0 -8
- package/dist/boilerplate/CLAUDE.md +0 -26
- package/dist/boilerplate/README.md +0 -12
- package/dist/boilerplate/_gitignore +0 -8
- package/dist/boilerplate/biome.json +0 -45
- package/dist/boilerplate/messages/en.json +0 -9
- package/dist/boilerplate/messages/ko.json +0 -9
- package/dist/boilerplate/package.json +0 -46
- package/dist/boilerplate/public/_headers +0 -5
- package/dist/boilerplate/public/favicon.svg +0 -1
- package/dist/boilerplate/src/hooks/use-catalog.ts +0 -28
- package/dist/boilerplate/src/i18n/index.ts +0 -99
- package/dist/boilerplate/src/i18n/locale.server.ts +0 -10
- package/dist/boilerplate/src/lib/analytics.ts +0 -60
- package/dist/boilerplate/src/lib/api.server.ts +0 -27
- package/dist/boilerplate/src/lib/api.ts +0 -7
- package/dist/boilerplate/src/lib/config.server.ts +0 -23
- package/dist/boilerplate/src/lib/config.test.ts +0 -16
- package/dist/boilerplate/src/lib/config.ts +0 -19
- package/dist/boilerplate/src/lib/cookies.server.ts +0 -35
- package/dist/boilerplate/src/lib/format.ts +0 -19
- package/dist/boilerplate/src/lib/queries.ts +0 -61
- package/dist/boilerplate/src/routeTree.gen.ts +0 -69
- package/dist/boilerplate/src/router.tsx +0 -44
- package/dist/boilerplate/src/routes/__root.tsx +0 -119
- package/dist/boilerplate/src/routes/index.tsx +0 -52
- package/dist/boilerplate/src/start.ts +0 -20
- package/dist/boilerplate/src/styles.css +0 -29
- package/dist/boilerplate/src/theme.css +0 -38
- package/dist/boilerplate/tsconfig.json +0 -16
- package/dist/boilerplate/vite.config.ts +0 -12
- package/dist/boilerplate/vitest.config.ts +0 -5
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { APP_DEPLOY_KEY_PATTERN, APP_README_MAX_BYTES, APP_SECRET_PATTERNS, APP_
|
|
|
6
6
|
import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { createServer } from "node:http";
|
|
8
8
|
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
10
|
import { gzipSync } from "node:zlib";
|
|
11
11
|
import { createHash, randomBytes } from "node:crypto";
|
|
12
12
|
|
|
@@ -1082,7 +1082,7 @@ const mcp_report_next_reverify_remote = ((inputs = {}, options = {}) => {
|
|
|
1082
1082
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1083
1083
|
/** @typedef {{ files: NonNullable<unknown> }} Mcp_Report_Template_Hint_LocalInputs */
|
|
1084
1084
|
const ko_mcp_report_template_hint_local = (i) => {
|
|
1085
|
-
return `손대지 않아야 하는 파일을 고친 결과일 수 있다 —
|
|
1085
|
+
return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) 규칙의 \`example\`이나 git 기록과 대조해 되돌린다.`;
|
|
1086
1086
|
};
|
|
1087
1087
|
/**
|
|
1088
1088
|
|
|
@@ -1100,7 +1100,7 @@ const mcp_report_template_hint_local = ((inputs, options = {}) => {
|
|
|
1100
1100
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1101
1101
|
/** @typedef {{ files: NonNullable<unknown> }} Mcp_Report_Template_Hint_RemoteInputs */
|
|
1102
1102
|
const ko_mcp_report_template_hint_remote = (i) => {
|
|
1103
|
-
return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) \`read_file\`로 읽어
|
|
1103
|
+
return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) \`read_file\`로 읽어 규칙의 \`example\`과 대조하고 되돌린다.`;
|
|
1104
1104
|
};
|
|
1105
1105
|
/**
|
|
1106
1106
|
|
|
@@ -1985,7 +1985,7 @@ const mcp_rule_local_import_exists_example = ((inputs = {}, options = {}) => {
|
|
|
1985
1985
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1986
1986
|
/** @typedef {{}} Mcp_Rule_Local_Import_Exists_FixInputs */
|
|
1987
1987
|
const ko_mcp_rule_local_import_exists_fix = () => {
|
|
1988
|
-
return `빠진 파일을
|
|
1988
|
+
return `빠진 파일을 같은 경로에 다시 만든다(git 기록에 있으면 되살린다). 호스팅 사이트는 \`write_files\`(\`expectedHash: null\`)나 \`sayren deploy\`로 올린다. 쓰지 않는 import면 지운다. 화면 문구 카탈로그는 \`messages/ko.json\`이다`;
|
|
1989
1989
|
};
|
|
1990
1990
|
/**
|
|
1991
1991
|
|
|
@@ -2751,42 +2751,6 @@ const mcp_rule_tenant_header_why = ((inputs = {}, options = {}) => {
|
|
|
2751
2751
|
return ko_mcp_rule_tenant_header_why(inputs);
|
|
2752
2752
|
});
|
|
2753
2753
|
|
|
2754
|
-
//#endregion
|
|
2755
|
-
//#region ../mcp/src/paraglide/messages/mcp_template_catalog_version_missing.js
|
|
2756
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
2757
|
-
/** @typedef {{ name: NonNullable<unknown> }} Mcp_Template_Catalog_Version_MissingInputs */
|
|
2758
|
-
const ko_mcp_template_catalog_version_missing = (i) => {
|
|
2759
|
-
return `catalog에 ${i?.name} 버전이 없습니다`;
|
|
2760
|
-
};
|
|
2761
|
-
/**
|
|
2762
|
-
|
|
2763
|
-
* @param {Mcp_Template_Catalog_Version_MissingInputs} inputs
|
|
2764
|
-
* @param {{ locale?: "ko" }} options
|
|
2765
|
-
* @returns {LocalizedString}
|
|
2766
|
-
*/
|
|
2767
|
-
const mcp_template_catalog_version_missing = ((inputs, options = {}) => {
|
|
2768
|
-
experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
|
|
2769
|
-
return ko_mcp_template_catalog_version_missing(inputs);
|
|
2770
|
-
});
|
|
2771
|
-
|
|
2772
|
-
//#endregion
|
|
2773
|
-
//#region ../mcp/src/paraglide/messages/mcp_template_published_version_unknown.js
|
|
2774
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
2775
|
-
/** @typedef {{ name: NonNullable<unknown> }} Mcp_Template_Published_Version_UnknownInputs */
|
|
2776
|
-
const ko_mcp_template_published_version_unknown = (i) => {
|
|
2777
|
-
return `${i?.name}의 배포 버전을 모르겠습니다`;
|
|
2778
|
-
};
|
|
2779
|
-
/**
|
|
2780
|
-
|
|
2781
|
-
* @param {Mcp_Template_Published_Version_UnknownInputs} inputs
|
|
2782
|
-
* @param {{ locale?: "ko" }} options
|
|
2783
|
-
* @returns {LocalizedString}
|
|
2784
|
-
*/
|
|
2785
|
-
const mcp_template_published_version_unknown = ((inputs, options = {}) => {
|
|
2786
|
-
experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
|
|
2787
|
-
return ko_mcp_template_published_version_unknown(inputs);
|
|
2788
|
-
});
|
|
2789
|
-
|
|
2790
2754
|
//#endregion
|
|
2791
2755
|
//#region ../mcp/src/paraglide/messages/mcp_verify_analytics_ids.js
|
|
2792
2756
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -3519,7 +3483,7 @@ function lazyMessages(table) {
|
|
|
3519
3483
|
/**
|
|
3520
3484
|
* 셀러 프로젝트의 `.mcp.json`(Claude Code 형식) — 원격 MCP(`mcp.sayren.io`)에 붙는 `sayren` 항목을 만들고 읽는다.
|
|
3521
3485
|
*
|
|
3522
|
-
* `npx sayren pull`·`login`(packages/cli)
|
|
3486
|
+
* `npx sayren pull`·`link`·`login`(packages/cli)이 같은 함수로 쓴다. 파일 입출력은 부르는 쪽이 하고 여기는 문자열만
|
|
3523
3487
|
* 다룬다 — 게이트웨이(Worker)가 헤더 이름을 이 파일에서 가져가므로 Node 전용 API를 쓰지 않는다.
|
|
3524
3488
|
*
|
|
3525
3489
|
* - **토큰을 쓰지 않는다(#71)** — 항목은 주소와 `X-Store-Id`뿐이고 MCP 클라이언트가 게이트웨이의 401 + `resource_metadata`를 보고
|
|
@@ -3609,7 +3573,7 @@ function sayrenStoreIdOf(text) {
|
|
|
3609
3573
|
//#endregion
|
|
3610
3574
|
//#region ../mcp/src/mcp-json-file.ts
|
|
3611
3575
|
/**
|
|
3612
|
-
* `.mcp.json` 파일 쓰기(Node) — `sayren` CLI(packages/cli)
|
|
3576
|
+
* `.mcp.json` 파일 쓰기(Node) — `sayren` CLI(packages/cli)가 쓴다. 형식은 `mcp-json.ts`(Node 없음)다.
|
|
3613
3577
|
* 항목에 토큰을 쓰지 않는다(#71 R1). 심볼릭 링크는 따라가지 않는다(폴더 밖 파일을 덮지 않게).
|
|
3614
3578
|
*/
|
|
3615
3579
|
function git(dir, args) {
|
|
@@ -5568,14 +5532,14 @@ const cli_create_dir_not_empty = ((inputs, options = {}) => {
|
|
|
5568
5532
|
//#endregion
|
|
5569
5533
|
//#region src/paraglide/messages/cli_create_done.js
|
|
5570
5534
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5571
|
-
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown> }} Cli_Create_DoneInputs */
|
|
5535
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Create_DoneInputs */
|
|
5572
5536
|
const ko_cli_create_done = (i) => {
|
|
5573
5537
|
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개)
|
|
5574
5538
|
다음:
|
|
5575
5539
|
cd ${i?.dir}
|
|
5576
|
-
|
|
5540
|
+
${i?.install}
|
|
5577
5541
|
npx sayren link # 상점에 연결하고 .env에 상점 값을 씁니다
|
|
5578
|
-
|
|
5542
|
+
${i?.dev}`;
|
|
5579
5543
|
};
|
|
5580
5544
|
/**
|
|
5581
5545
|
|
|
@@ -5591,13 +5555,13 @@ const cli_create_done = ((inputs, options = {}) => {
|
|
|
5591
5555
|
//#endregion
|
|
5592
5556
|
//#region src/paraglide/messages/cli_create_done_store_code.js
|
|
5593
5557
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5594
|
-
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
|
|
5558
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
|
|
5595
5559
|
const ko_cli_create_done_store_code = (i) => {
|
|
5596
5560
|
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개, .env에 상점 ${i?.storeCode} 연결 값)
|
|
5597
5561
|
다음:
|
|
5598
5562
|
cd ${i?.dir}
|
|
5599
|
-
|
|
5600
|
-
|
|
5563
|
+
${i?.install}
|
|
5564
|
+
${i?.dev}
|
|
5601
5565
|
npx sayren deploy # 처음이면 상점 사이트에 연결한 뒤 배포합니다`;
|
|
5602
5566
|
};
|
|
5603
5567
|
/**
|
|
@@ -5629,6 +5593,42 @@ const cli_create_name_invalid = ((inputs, options = {}) => {
|
|
|
5629
5593
|
return ko_cli_create_name_invalid(inputs);
|
|
5630
5594
|
});
|
|
5631
5595
|
|
|
5596
|
+
//#endregion
|
|
5597
|
+
//#region src/paraglide/messages/cli_create_starter_no_release.js
|
|
5598
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5599
|
+
/** @typedef {{ status: NonNullable<unknown> }} Cli_Create_Starter_No_ReleaseInputs */
|
|
5600
|
+
const ko_cli_create_starter_no_release = (i) => {
|
|
5601
|
+
return `시작 코드의 최신 릴리스를 찾지 못했습니다(GitHub avarcorp/sayren-starter, HTTP ${i?.status}). 잠시 뒤 다시 시도하십시오`;
|
|
5602
|
+
};
|
|
5603
|
+
/**
|
|
5604
|
+
|
|
5605
|
+
* @param {Cli_Create_Starter_No_ReleaseInputs} inputs
|
|
5606
|
+
* @param {{ locale?: "ko" }} options
|
|
5607
|
+
* @returns {LocalizedString}
|
|
5608
|
+
*/
|
|
5609
|
+
const cli_create_starter_no_release = ((inputs, options = {}) => {
|
|
5610
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5611
|
+
return ko_cli_create_starter_no_release(inputs);
|
|
5612
|
+
});
|
|
5613
|
+
|
|
5614
|
+
//#endregion
|
|
5615
|
+
//#region src/paraglide/messages/cli_create_starter_unreachable.js
|
|
5616
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5617
|
+
/** @typedef {{}} Cli_Create_Starter_UnreachableInputs */
|
|
5618
|
+
const ko_cli_create_starter_unreachable = () => {
|
|
5619
|
+
return `시작 코드의 최신 버전을 확인하지 못했습니다. 시작 코드는 GitHub(avarcorp/sayren-starter)에서 받으므로 네트워크 연결을 확인하십시오`;
|
|
5620
|
+
};
|
|
5621
|
+
/**
|
|
5622
|
+
|
|
5623
|
+
* @param {Cli_Create_Starter_UnreachableInputs} inputs
|
|
5624
|
+
* @param {{ locale?: "ko" }} options
|
|
5625
|
+
* @returns {LocalizedString}
|
|
5626
|
+
*/
|
|
5627
|
+
const cli_create_starter_unreachable = ((inputs = {}, options = {}) => {
|
|
5628
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5629
|
+
return ko_cli_create_starter_unreachable(inputs);
|
|
5630
|
+
});
|
|
5631
|
+
|
|
5632
5632
|
//#endregion
|
|
5633
5633
|
//#region src/paraglide/messages/cli_create_store_code_invalid.js
|
|
5634
5634
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -6011,9 +6011,9 @@ const cli_deploy_build_local_failed = ((inputs, options = {}) => {
|
|
|
6011
6011
|
//#endregion
|
|
6012
6012
|
//#region src/paraglide/messages/cli_deploy_build_local_no_deps.js
|
|
6013
6013
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6014
|
-
/** @typedef {{}} Cli_Deploy_Build_Local_No_DepsInputs */
|
|
6015
|
-
const ko_cli_deploy_build_local_no_deps = () => {
|
|
6016
|
-
return `의존성이 설치되지 않아(node_modules 없음) 로컬 빌드를 건너뜁니다.
|
|
6014
|
+
/** @typedef {{ install: NonNullable<unknown> }} Cli_Deploy_Build_Local_No_DepsInputs */
|
|
6015
|
+
const ko_cli_deploy_build_local_no_deps = (i) => {
|
|
6016
|
+
return `의존성이 설치되지 않아(node_modules 없음) 로컬 빌드를 건너뜁니다. ${i?.install} 뒤 배포하면 올리기 전에 빌드를 확인합니다`;
|
|
6017
6017
|
};
|
|
6018
6018
|
/**
|
|
6019
6019
|
|
|
@@ -6021,7 +6021,7 @@ const ko_cli_deploy_build_local_no_deps = () => {
|
|
|
6021
6021
|
* @param {{ locale?: "ko" }} options
|
|
6022
6022
|
* @returns {LocalizedString}
|
|
6023
6023
|
*/
|
|
6024
|
-
const cli_deploy_build_local_no_deps = ((inputs
|
|
6024
|
+
const cli_deploy_build_local_no_deps = ((inputs, options = {}) => {
|
|
6025
6025
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6026
6026
|
return ko_cli_deploy_build_local_no_deps(inputs);
|
|
6027
6027
|
});
|
|
@@ -6856,274 +6856,21 @@ const cli_flag_value_required = ((inputs, options = {}) => {
|
|
|
6856
6856
|
});
|
|
6857
6857
|
|
|
6858
6858
|
//#endregion
|
|
6859
|
-
//#region src/paraglide/messages/
|
|
6859
|
+
//#region src/paraglide/messages/cli_init_retired.js
|
|
6860
6860
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6861
|
-
/** @typedef {{}}
|
|
6862
|
-
const
|
|
6863
|
-
return
|
|
6861
|
+
/** @typedef {{}} Cli_Init_RetiredInputs */
|
|
6862
|
+
const ko_cli_init_retired = () => {
|
|
6863
|
+
return `npx sayren init은 더는 사이트를 만들지 않습니다. npx sayren create <이름>으로 시작 코드를 만든 뒤 그 폴더에서 npx sayren link로 사이트를 만들고 연결하십시오`;
|
|
6864
6864
|
};
|
|
6865
6865
|
/**
|
|
6866
6866
|
|
|
6867
|
-
* @param {
|
|
6867
|
+
* @param {Cli_Init_RetiredInputs} inputs
|
|
6868
6868
|
* @param {{ locale?: "ko" }} options
|
|
6869
6869
|
* @returns {LocalizedString}
|
|
6870
6870
|
*/
|
|
6871
|
-
const
|
|
6871
|
+
const cli_init_retired = ((inputs = {}, options = {}) => {
|
|
6872
6872
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6873
|
-
return
|
|
6874
|
-
});
|
|
6875
|
-
|
|
6876
|
-
//#endregion
|
|
6877
|
-
//#region src/paraglide/messages/cli_init_created.js
|
|
6878
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6879
|
-
/** @typedef {{ url: NonNullable<unknown> }} Cli_Init_CreatedInputs */
|
|
6880
|
-
const ko_cli_init_created = (i) => {
|
|
6881
|
-
return `사이트를 만들었습니다 — ${i?.url} (오픈 준비 중, 반영까지 1분 안팎)`;
|
|
6882
|
-
};
|
|
6883
|
-
/**
|
|
6884
|
-
|
|
6885
|
-
* @param {Cli_Init_CreatedInputs} inputs
|
|
6886
|
-
* @param {{ locale?: "ko" }} options
|
|
6887
|
-
* @returns {LocalizedString}
|
|
6888
|
-
*/
|
|
6889
|
-
const cli_init_created = ((inputs, options = {}) => {
|
|
6890
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6891
|
-
return ko_cli_init_created(inputs);
|
|
6892
|
-
});
|
|
6893
|
-
|
|
6894
|
-
//#endregion
|
|
6895
|
-
//#region src/paraglide/messages/cli_init_dir_not_empty.js
|
|
6896
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6897
|
-
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Init_Dir_Not_EmptyInputs */
|
|
6898
|
-
const ko_cli_init_dir_not_empty = (i) => {
|
|
6899
|
-
return `폴더가 비어 있지 않습니다: ${i?.dir}. 빈 폴더나 새 폴더 이름을 주십시오`;
|
|
6900
|
-
};
|
|
6901
|
-
/**
|
|
6902
|
-
|
|
6903
|
-
* @param {Cli_Init_Dir_Not_EmptyInputs} inputs
|
|
6904
|
-
* @param {{ locale?: "ko" }} options
|
|
6905
|
-
* @returns {LocalizedString}
|
|
6906
|
-
*/
|
|
6907
|
-
const cli_init_dir_not_empty = ((inputs, options = {}) => {
|
|
6908
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6909
|
-
return ko_cli_init_dir_not_empty(inputs);
|
|
6910
|
-
});
|
|
6911
|
-
|
|
6912
|
-
//#endregion
|
|
6913
|
-
//#region src/paraglide/messages/cli_init_inside_pulled.js
|
|
6914
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6915
|
-
/** @typedef {{}} Cli_Init_Inside_PulledInputs */
|
|
6916
|
-
const ko_cli_init_inside_pulled = () => {
|
|
6917
|
-
return `지금 폴더는 이미 받은 사이트 폴더입니다. 새 사이트는 다른 폴더에서 npx sayren init <폴더>로 만들고, 이 사이트를 다시 받으려면 npx sayren pull --force를 쓰십시오`;
|
|
6918
|
-
};
|
|
6919
|
-
/**
|
|
6920
|
-
|
|
6921
|
-
* @param {Cli_Init_Inside_PulledInputs} inputs
|
|
6922
|
-
* @param {{ locale?: "ko" }} options
|
|
6923
|
-
* @returns {LocalizedString}
|
|
6924
|
-
*/
|
|
6925
|
-
const cli_init_inside_pulled = ((inputs = {}, options = {}) => {
|
|
6926
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6927
|
-
return ko_cli_init_inside_pulled(inputs);
|
|
6928
|
-
});
|
|
6929
|
-
|
|
6930
|
-
//#endregion
|
|
6931
|
-
//#region src/paraglide/messages/cli_init_no_templates.js
|
|
6932
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6933
|
-
/** @typedef {{}} Cli_Init_No_TemplatesInputs */
|
|
6934
|
-
const ko_cli_init_no_templates = () => {
|
|
6935
|
-
return `쓸 수 있는 템플릿이 없습니다`;
|
|
6936
|
-
};
|
|
6937
|
-
/**
|
|
6938
|
-
|
|
6939
|
-
* @param {Cli_Init_No_TemplatesInputs} inputs
|
|
6940
|
-
* @param {{ locale?: "ko" }} options
|
|
6941
|
-
* @returns {LocalizedString}
|
|
6942
|
-
*/
|
|
6943
|
-
const cli_init_no_templates = ((inputs = {}, options = {}) => {
|
|
6944
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6945
|
-
return ko_cli_init_no_templates(inputs);
|
|
6946
|
-
});
|
|
6947
|
-
|
|
6948
|
-
//#endregion
|
|
6949
|
-
//#region src/paraglide/messages/cli_init_pull_failed.js
|
|
6950
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6951
|
-
/** @typedef {{ storeId: NonNullable<unknown> }} Cli_Init_Pull_FailedInputs */
|
|
6952
|
-
const ko_cli_init_pull_failed = (i) => {
|
|
6953
|
-
return `사이트는 만들었지만 소스를 받지 못했습니다. 다시 만들지 말고 npx sayren pull --project ${i?.storeId}로 받으십시오`;
|
|
6954
|
-
};
|
|
6955
|
-
/**
|
|
6956
|
-
|
|
6957
|
-
* @param {Cli_Init_Pull_FailedInputs} inputs
|
|
6958
|
-
* @param {{ locale?: "ko" }} options
|
|
6959
|
-
* @returns {LocalizedString}
|
|
6960
|
-
*/
|
|
6961
|
-
const cli_init_pull_failed = ((inputs, options = {}) => {
|
|
6962
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6963
|
-
return ko_cli_init_pull_failed(inputs);
|
|
6964
|
-
});
|
|
6965
|
-
|
|
6966
|
-
//#endregion
|
|
6967
|
-
//#region src/paraglide/messages/cli_init_site_exists.js
|
|
6968
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6969
|
-
/** @typedef {{ storeId: NonNullable<unknown> }} Cli_Init_Site_ExistsInputs */
|
|
6970
|
-
const ko_cli_init_site_exists = (i) => {
|
|
6971
|
-
return `이 상점에는 이미 호스팅 사이트가 있어 새로 만들지 않았습니다. npx sayren pull --project ${i?.storeId}로 소스를 받으십시오`;
|
|
6972
|
-
};
|
|
6973
|
-
/**
|
|
6974
|
-
|
|
6975
|
-
* @param {Cli_Init_Site_ExistsInputs} inputs
|
|
6976
|
-
* @param {{ locale?: "ko" }} options
|
|
6977
|
-
* @returns {LocalizedString}
|
|
6978
|
-
*/
|
|
6979
|
-
const cli_init_site_exists = ((inputs, options = {}) => {
|
|
6980
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6981
|
-
return ko_cli_init_site_exists(inputs);
|
|
6982
|
-
});
|
|
6983
|
-
|
|
6984
|
-
//#endregion
|
|
6985
|
-
//#region src/paraglide/messages/cli_init_site_url.js
|
|
6986
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6987
|
-
/** @typedef {{ url: NonNullable<unknown> }} Cli_Init_Site_UrlInputs */
|
|
6988
|
-
const ko_cli_init_site_url = (i) => {
|
|
6989
|
-
return `지금 사이트: ${i?.url}`;
|
|
6990
|
-
};
|
|
6991
|
-
/**
|
|
6992
|
-
|
|
6993
|
-
* @param {Cli_Init_Site_UrlInputs} inputs
|
|
6994
|
-
* @param {{ locale?: "ko" }} options
|
|
6995
|
-
* @returns {LocalizedString}
|
|
6996
|
-
*/
|
|
6997
|
-
const cli_init_site_url = ((inputs, options = {}) => {
|
|
6998
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6999
|
-
return ko_cli_init_site_url(inputs);
|
|
7000
|
-
});
|
|
7001
|
-
|
|
7002
|
-
//#endregion
|
|
7003
|
-
//#region src/paraglide/messages/cli_init_slug_taken.js
|
|
7004
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7005
|
-
/** @typedef {{}} Cli_Init_Slug_TakenInputs */
|
|
7006
|
-
const ko_cli_init_slug_taken = () => {
|
|
7007
|
-
return `상점 코드와 같은 주소를 다른 사이트가 쓰고 있어 만들지 못했습니다. 상점 플랫폼 › 스토어프론트에서 다른 주소로 만든 뒤 npx sayren pull로 받으십시오`;
|
|
7008
|
-
};
|
|
7009
|
-
/**
|
|
7010
|
-
|
|
7011
|
-
* @param {Cli_Init_Slug_TakenInputs} inputs
|
|
7012
|
-
* @param {{ locale?: "ko" }} options
|
|
7013
|
-
* @returns {LocalizedString}
|
|
7014
|
-
*/
|
|
7015
|
-
const cli_init_slug_taken = ((inputs = {}, options = {}) => {
|
|
7016
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7017
|
-
return ko_cli_init_slug_taken(inputs);
|
|
7018
|
-
});
|
|
7019
|
-
|
|
7020
|
-
//#endregion
|
|
7021
|
-
//#region src/paraglide/messages/cli_init_summary.js
|
|
7022
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7023
|
-
/** @typedef {{ storeId: NonNullable<unknown>, name: NonNullable<unknown>, templateId: NonNullable<unknown>, version: NonNullable<unknown> }} Cli_Init_SummaryInputs */
|
|
7024
|
-
const ko_cli_init_summary = (i) => {
|
|
7025
|
-
return `상점 ${i?.storeId}에 템플릿 ${i?.name}(${i?.templateId} v${i?.version})로 호스팅 사이트를 만듭니다. 주소는 상점 코드이고 오픈 준비 중으로 시작합니다. 만들기는 상점당 30일에 3번까지입니다`;
|
|
7026
|
-
};
|
|
7027
|
-
/**
|
|
7028
|
-
|
|
7029
|
-
* @param {Cli_Init_SummaryInputs} inputs
|
|
7030
|
-
* @param {{ locale?: "ko" }} options
|
|
7031
|
-
* @returns {LocalizedString}
|
|
7032
|
-
*/
|
|
7033
|
-
const cli_init_summary = ((inputs, options = {}) => {
|
|
7034
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7035
|
-
return ko_cli_init_summary(inputs);
|
|
7036
|
-
});
|
|
7037
|
-
|
|
7038
|
-
//#endregion
|
|
7039
|
-
//#region src/paraglide/messages/cli_init_template_option.js
|
|
7040
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7041
|
-
/** @typedef {{ templateId: NonNullable<unknown>, name: NonNullable<unknown>, version: NonNullable<unknown>, description: NonNullable<unknown> }} Cli_Init_Template_OptionInputs */
|
|
7042
|
-
const ko_cli_init_template_option = (i) => {
|
|
7043
|
-
return `${i?.templateId} — ${i?.name} v${i?.version}: ${i?.description}`;
|
|
7044
|
-
};
|
|
7045
|
-
/**
|
|
7046
|
-
|
|
7047
|
-
* @param {Cli_Init_Template_OptionInputs} inputs
|
|
7048
|
-
* @param {{ locale?: "ko" }} options
|
|
7049
|
-
* @returns {LocalizedString}
|
|
7050
|
-
*/
|
|
7051
|
-
const cli_init_template_option = ((inputs, options = {}) => {
|
|
7052
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7053
|
-
return ko_cli_init_template_option(inputs);
|
|
7054
|
-
});
|
|
7055
|
-
|
|
7056
|
-
//#endregion
|
|
7057
|
-
//#region src/paraglide/messages/cli_init_template_prompt.js
|
|
7058
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7059
|
-
/** @typedef {{}} Cli_Init_Template_PromptInputs */
|
|
7060
|
-
const ko_cli_init_template_prompt = () => {
|
|
7061
|
-
return `템플릿을 고르십시오:`;
|
|
7062
|
-
};
|
|
7063
|
-
/**
|
|
7064
|
-
|
|
7065
|
-
* @param {Cli_Init_Template_PromptInputs} inputs
|
|
7066
|
-
* @param {{ locale?: "ko" }} options
|
|
7067
|
-
* @returns {LocalizedString}
|
|
7068
|
-
*/
|
|
7069
|
-
const cli_init_template_prompt = ((inputs = {}, options = {}) => {
|
|
7070
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7071
|
-
return ko_cli_init_template_prompt(inputs);
|
|
7072
|
-
});
|
|
7073
|
-
|
|
7074
|
-
//#endregion
|
|
7075
|
-
//#region src/paraglide/messages/cli_init_template_required.js
|
|
7076
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7077
|
-
/** @typedef {{ list: NonNullable<unknown> }} Cli_Init_Template_RequiredInputs */
|
|
7078
|
-
const ko_cli_init_template_required = (i) => {
|
|
7079
|
-
return `템플릿을 고르지 않았습니다. --template <id>로 주십시오(비대화형은 필수입니다):
|
|
7080
|
-
${i?.list}`;
|
|
7081
|
-
};
|
|
7082
|
-
/**
|
|
7083
|
-
|
|
7084
|
-
* @param {Cli_Init_Template_RequiredInputs} inputs
|
|
7085
|
-
* @param {{ locale?: "ko" }} options
|
|
7086
|
-
* @returns {LocalizedString}
|
|
7087
|
-
*/
|
|
7088
|
-
const cli_init_template_required = ((inputs, options = {}) => {
|
|
7089
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7090
|
-
return ko_cli_init_template_required(inputs);
|
|
7091
|
-
});
|
|
7092
|
-
|
|
7093
|
-
//#endregion
|
|
7094
|
-
//#region src/paraglide/messages/cli_init_template_unknown.js
|
|
7095
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7096
|
-
/** @typedef {{ templateId: NonNullable<unknown>, ids: NonNullable<unknown> }} Cli_Init_Template_UnknownInputs */
|
|
7097
|
-
const ko_cli_init_template_unknown = (i) => {
|
|
7098
|
-
return `템플릿을 찾지 못했습니다: ${i?.templateId}. 쓸 수 있는 템플릿: ${i?.ids}`;
|
|
7099
|
-
};
|
|
7100
|
-
/**
|
|
7101
|
-
|
|
7102
|
-
* @param {Cli_Init_Template_UnknownInputs} inputs
|
|
7103
|
-
* @param {{ locale?: "ko" }} options
|
|
7104
|
-
* @returns {LocalizedString}
|
|
7105
|
-
*/
|
|
7106
|
-
const cli_init_template_unknown = ((inputs, options = {}) => {
|
|
7107
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7108
|
-
return ko_cli_init_template_unknown(inputs);
|
|
7109
|
-
});
|
|
7110
|
-
|
|
7111
|
-
//#endregion
|
|
7112
|
-
//#region src/paraglide/messages/cli_init_yes_required.js
|
|
7113
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7114
|
-
/** @typedef {{}} Cli_Init_Yes_RequiredInputs */
|
|
7115
|
-
const ko_cli_init_yes_required = () => {
|
|
7116
|
-
return `사이트 만들기를 확인할 수 없는 환경입니다. --yes를 함께 주십시오`;
|
|
7117
|
-
};
|
|
7118
|
-
/**
|
|
7119
|
-
|
|
7120
|
-
* @param {Cli_Init_Yes_RequiredInputs} inputs
|
|
7121
|
-
* @param {{ locale?: "ko" }} options
|
|
7122
|
-
* @returns {LocalizedString}
|
|
7123
|
-
*/
|
|
7124
|
-
const cli_init_yes_required = ((inputs = {}, options = {}) => {
|
|
7125
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7126
|
-
return ko_cli_init_yes_required(inputs);
|
|
6873
|
+
return ko_cli_init_retired(inputs);
|
|
7127
6874
|
});
|
|
7128
6875
|
|
|
7129
6876
|
//#endregion
|
|
@@ -7309,11 +7056,11 @@ const cli_link_gitignore = ((inputs, options = {}) => {
|
|
|
7309
7056
|
//#endregion
|
|
7310
7057
|
//#region src/paraglide/messages/cli_link_next_steps.js
|
|
7311
7058
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7312
|
-
/** @typedef {{}} Cli_Link_Next_StepsInputs */
|
|
7313
|
-
const ko_cli_link_next_steps = () => {
|
|
7059
|
+
/** @typedef {{ install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Link_Next_StepsInputs */
|
|
7060
|
+
const ko_cli_link_next_steps = (i) => {
|
|
7314
7061
|
return `다음:
|
|
7315
|
-
|
|
7316
|
-
|
|
7062
|
+
${i?.install} # 의존성의 설치 스크립트를 돌리지 않습니다
|
|
7063
|
+
${i?.dev} # http://localhost:4010 — 운영 상점 데이터로 동작합니다
|
|
7317
7064
|
npx sayren deploy # 빌드 → 버전 → 발행`;
|
|
7318
7065
|
};
|
|
7319
7066
|
/**
|
|
@@ -7322,7 +7069,7 @@ const ko_cli_link_next_steps = () => {
|
|
|
7322
7069
|
* @param {{ locale?: "ko" }} options
|
|
7323
7070
|
* @returns {LocalizedString}
|
|
7324
7071
|
*/
|
|
7325
|
-
const cli_link_next_steps = ((inputs
|
|
7072
|
+
const cli_link_next_steps = ((inputs, options = {}) => {
|
|
7326
7073
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7327
7074
|
return ko_cli_link_next_steps(inputs);
|
|
7328
7075
|
});
|
|
@@ -8248,13 +7995,13 @@ const cli_pull_mcp_written = ((inputs, options = {}) => {
|
|
|
8248
7995
|
//#endregion
|
|
8249
7996
|
//#region src/paraglide/messages/cli_pull_next_steps.js
|
|
8250
7997
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8251
|
-
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Pull_Next_StepsInputs */
|
|
7998
|
+
/** @typedef {{ dir: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Pull_Next_StepsInputs */
|
|
8252
7999
|
const ko_cli_pull_next_steps = (i) => {
|
|
8253
8000
|
return `
|
|
8254
8001
|
다음:
|
|
8255
8002
|
cd ${i?.dir}
|
|
8256
|
-
|
|
8257
|
-
|
|
8003
|
+
${i?.install} # 의존성의 설치 스크립트를 돌리지 않습니다
|
|
8004
|
+
${i?.dev} # http://localhost:4010 — 운영 상점 데이터로 동작합니다
|
|
8258
8005
|
npx sayren deploy # 빌드 → 버전 → 발행`;
|
|
8259
8006
|
};
|
|
8260
8007
|
/**
|
|
@@ -8273,7 +8020,7 @@ const cli_pull_next_steps = ((inputs, options = {}) => {
|
|
|
8273
8020
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8274
8021
|
/** @typedef {{}} Cli_Pull_No_SiteInputs */
|
|
8275
8022
|
const ko_cli_pull_no_site = () => {
|
|
8276
|
-
return `이 상점에는 호스팅 사이트가 없습니다. npx sayren
|
|
8023
|
+
return `이 상점에는 호스팅 사이트가 없습니다. npx sayren link로 만들거나 상점 플랫폼 › 스토어프론트에서 만든 뒤 다시 받으십시오`;
|
|
8277
8024
|
};
|
|
8278
8025
|
/**
|
|
8279
8026
|
|
|
@@ -8379,9 +8126,9 @@ const cli_pull_store_mismatch = ((inputs, options = {}) => {
|
|
|
8379
8126
|
//#endregion
|
|
8380
8127
|
//#region src/paraglide/messages/cli_pull_watched_changed.js
|
|
8381
8128
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8382
|
-
/** @typedef {{ path: NonNullable<unknown> }} Cli_Pull_Watched_ChangedInputs */
|
|
8129
|
+
/** @typedef {{ path: NonNullable<unknown>, install: NonNullable<unknown> }} Cli_Pull_Watched_ChangedInputs */
|
|
8383
8130
|
const ko_cli_pull_watched_changed = (i) => {
|
|
8384
|
-
return `알림: 원격의 ${i?.path}가 이 폴더와 다릅니다. 설치 전에 내용을 확인하십시오(
|
|
8131
|
+
return `알림: 원격의 ${i?.path}가 이 폴더와 다릅니다. 설치 전에 내용을 확인하십시오(${i?.install} 권장)`;
|
|
8385
8132
|
};
|
|
8386
8133
|
/**
|
|
8387
8134
|
|
|
@@ -9355,7 +9102,6 @@ const ko_cli_usage = (i) => {
|
|
|
9355
9102
|
npx sayren create <이름> [--template <템플릿>] # 시작 코드를 새 폴더에 만듭니다(파일만, 로그인 없음)
|
|
9356
9103
|
npx sayren link [폴더] [--store <storeId>] # 폴더를 상점 사이트에 연결합니다(없으면 사이트를 만듭니다)
|
|
9357
9104
|
npx sayren env pull # 연결된 상점 값을 .env에 씁니다
|
|
9358
|
-
npx sayren init [폴더] [--template <id>] [--store <storeId>] [--yes]
|
|
9359
9105
|
npx sayren pull --project <storeId> [폴더] [--force [--yes]]
|
|
9360
9106
|
npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
|
|
9361
9107
|
npx sayren status
|
|
@@ -9370,7 +9116,6 @@ const ko_cli_usage = (i) => {
|
|
|
9370
9116
|
create 시작 코드(상점 이름·상품 목록 홈, SDK·문구·분석·에이전트 안내)를 <이름> 폴더에 만듭니다. 상점을 묻지 않고 호스팅 사이트도 만들지 않습니다
|
|
9371
9117
|
link 상점을 고르고(--store → 토큰 범위 → 선택) 사이트가 없으면 만든 뒤 .sayren/project.json·.env·.mcp.json을 씁니다
|
|
9372
9118
|
env env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL을 .env에 씁니다
|
|
9373
|
-
init 템플릿을 골라 호스팅 사이트를 만들고 pull과 같은 폴더(기본: 상점 코드)로 받습니다. 사이트가 이미 있으면 만들지 않습니다
|
|
9374
9119
|
pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/project.json을 씁니다
|
|
9375
9120
|
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 연결이 없으면 link부터 하고 다음 버전으로 교체합니다. 원격이 마지막 받기·올리기 이후 바뀌었으면 덮어쓰지 않습니다
|
|
9376
9121
|
status 사이트·발행 버전·최근 빌드·편집 환경
|
|
@@ -9386,12 +9131,11 @@ const ko_cli_usage = (i) => {
|
|
|
9386
9131
|
옵션:
|
|
9387
9132
|
--project <storeId> pull: 받을 상점(--store와 같습니다)
|
|
9388
9133
|
--template <템플릿> create: github:owner/repo[/경로][#ref](공개 GitHub 저장소) 또는 템플릿 id. 없으면 CLI에 든 시작 코드
|
|
9389
|
-
init: 호스팅 템플릿 id(비대화형은 필수)
|
|
9390
9134
|
--store-code <코드> create: 이 상점 코드로 .env를 씁니다(없으면 link·env pull이 씁니다)
|
|
9391
|
-
--dir <폴더> 작업 폴더(기본: 지금 폴더, pull
|
|
9135
|
+
--dir <폴더> 작업 폴더(기본: 지금 폴더, pull은 상점 코드)
|
|
9392
9136
|
--force pull: 같은 사이트를 받은 폴더에 원격을 다시 받습니다(지울·덮을 파일을 보여 주고 묻습니다)
|
|
9393
9137
|
deploy: 원격 변경·열린 편집 환경을 무시하고 덮어씁니다(확인을 묻습니다)
|
|
9394
|
-
--yes --force
|
|
9138
|
+
--yes --force의 확인을 묻지 않습니다(비대화형)
|
|
9395
9139
|
--no-publish deploy: 버전까지만 만들고 발행하지 않습니다
|
|
9396
9140
|
--skip-build deploy: 올리기 전 로컬 빌드(npm run build)를 건너뜁니다
|
|
9397
9141
|
--message <메모> deploy: 버전 메모
|
|
@@ -10326,7 +10070,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
10326
10070
|
|
|
10327
10071
|
//#endregion
|
|
10328
10072
|
//#region package.json
|
|
10329
|
-
var version = "0.
|
|
10073
|
+
var version = "0.16.0";
|
|
10330
10074
|
|
|
10331
10075
|
//#endregion
|
|
10332
10076
|
//#region src/client.ts
|
|
@@ -12204,7 +11948,7 @@ function isNeverUpload(path) {
|
|
|
12204
11948
|
return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren" || segment === ".dev.vars" || segment.startsWith(".dev.vars.")) || name === ".mcp.json" || name === ".sayrenrc" || name === ".npmrc";
|
|
12205
11949
|
}
|
|
12206
11950
|
/** 걸어 들어가지 않는 폴더 — 설치물·산출물·캐시·VCS(경로 규칙이 어차피 막는다. 크기 때문에 읽지도 않는다) */
|
|
12207
|
-
const SKIP_DIRS
|
|
11951
|
+
const SKIP_DIRS = new Set([
|
|
12208
11952
|
"node_modules",
|
|
12209
11953
|
"dist",
|
|
12210
11954
|
".output",
|
|
@@ -12225,7 +11969,7 @@ function collectTree(root) {
|
|
|
12225
11969
|
const full = join(dir, entry.name);
|
|
12226
11970
|
const rel = normalizeStorefrontPath(relative(root, full).split(sep).join("/"));
|
|
12227
11971
|
if (entry.isDirectory()) {
|
|
12228
|
-
if (SKIP_DIRS
|
|
11972
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
12229
11973
|
walk(full);
|
|
12230
11974
|
continue;
|
|
12231
11975
|
}
|
|
@@ -12436,7 +12180,8 @@ function lockImporterSpecifiers(lock) {
|
|
|
12436
12180
|
//#region src/guide-skill.ts
|
|
12437
12181
|
/**
|
|
12438
12182
|
* 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 기능·화면을 고치기 전에 sayren 중앙 스킬을 `npx sayren skill`로 읽게 한다(스킬은 이 하나만 설치한다).
|
|
12439
|
-
*
|
|
12183
|
+
* 이 파일이 원천이다 — `create`는 받은 트리에 늘 이 내용을 쓰고(시작 코드 저장소에는 두지 않는다), `pull`·`init`은 없을 때만 넣는다(`withGuideSkill`).
|
|
12184
|
+
* sayren.io 디스커버리(`apps/website/app/lib/agent-skills.ts`)가 같은 내용을 내준다(`agent-skills.test.ts`가 고정한다).
|
|
12440
12185
|
*/
|
|
12441
12186
|
const GUIDE_SKILL_PATH = ".claude/skills/sayren/SKILL.md";
|
|
12442
12187
|
const GUIDE_SKILL = "---\nname: sayren\ndescription: Load before adding, implementing or changing any feature or page of this sayren storefront. Lists the current sayren skills and loads the ones the task needs.\n---\n\n# sayren skills\n\nThe sayren skills are maintained centrally and change without updating this project.\nDo not rely on memory or earlier copies. Load them at the start of each task.\n\n1. List the skills (name and when to use each):\n `npx -y sayren@latest skill list`\n2. Read every skill that matches the task, in full, and follow it:\n `npx -y sayren@latest skill <name>`\n\nIf the sayren MCP server is connected, the tools `list_skills` and `get_skill` return the same content.\n\nFollow the project's `CLAUDE.md` first. The skills add the sayren-specific details.\n";
|
|
@@ -12499,6 +12244,60 @@ function tailOf(output, lines = OUTPUT_TAIL_LINES) {
|
|
|
12499
12244
|
return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
|
|
12500
12245
|
}
|
|
12501
12246
|
|
|
12247
|
+
//#endregion
|
|
12248
|
+
//#region src/package-manager.ts
|
|
12249
|
+
const KNOWN = [
|
|
12250
|
+
"npm",
|
|
12251
|
+
"pnpm",
|
|
12252
|
+
"yarn",
|
|
12253
|
+
"bun"
|
|
12254
|
+
];
|
|
12255
|
+
const LOCKFILES = [
|
|
12256
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
12257
|
+
["yarn.lock", "yarn"],
|
|
12258
|
+
["bun.lock", "bun"],
|
|
12259
|
+
["bun.lockb", "bun"],
|
|
12260
|
+
["package-lock.json", "npm"]
|
|
12261
|
+
];
|
|
12262
|
+
/** `has(path)`는 그 파일이 있는지, `read(path)`는 텍스트(없으면 null)다 — 받은 트리(Map)와 디스크 폴더가 같은 판정을 쓴다 */
|
|
12263
|
+
function packageManagerOf(source) {
|
|
12264
|
+
const pkg = source.read("package.json");
|
|
12265
|
+
if (pkg) try {
|
|
12266
|
+
const field = JSON.parse(pkg).packageManager;
|
|
12267
|
+
const name = typeof field === "string" ? field.split("@")[0]?.trim() : void 0;
|
|
12268
|
+
if (name && KNOWN.includes(name)) return name;
|
|
12269
|
+
} catch {}
|
|
12270
|
+
for (const [file, manager] of LOCKFILES) if (source.has(file)) return manager;
|
|
12271
|
+
return "npm";
|
|
12272
|
+
}
|
|
12273
|
+
/** 받은 트리(경로 → 내용) */
|
|
12274
|
+
function packageManagerOfFiles(files) {
|
|
12275
|
+
return packageManagerOf({
|
|
12276
|
+
has: (path) => files.has(path),
|
|
12277
|
+
read: (path) => {
|
|
12278
|
+
const bytes = files.get(path);
|
|
12279
|
+
return bytes ? new TextDecoder().decode(bytes) : null;
|
|
12280
|
+
}
|
|
12281
|
+
});
|
|
12282
|
+
}
|
|
12283
|
+
/** 디스크 폴더 */
|
|
12284
|
+
function packageManagerOfDir(dir) {
|
|
12285
|
+
return packageManagerOf({
|
|
12286
|
+
has: (path) => existsSync(join(dir, path)),
|
|
12287
|
+
read: (path) => {
|
|
12288
|
+
try {
|
|
12289
|
+
return readFileSync(join(dir, path), "utf8");
|
|
12290
|
+
} catch {
|
|
12291
|
+
return null;
|
|
12292
|
+
}
|
|
12293
|
+
}
|
|
12294
|
+
});
|
|
12295
|
+
}
|
|
12296
|
+
/** 설치 스크립트를 돌리지 않는 설치 명령 */
|
|
12297
|
+
const installCommand = (manager) => `${manager} install --ignore-scripts`;
|
|
12298
|
+
/** 개발 서버 명령 — npm·bun은 `run`이 있어야 스크립트를 부른다 */
|
|
12299
|
+
const devCommand = (manager) => manager === "npm" || manager === "bun" ? `${manager} run dev` : `${manager} dev`;
|
|
12300
|
+
|
|
12502
12301
|
//#endregion
|
|
12503
12302
|
//#region src/commands.ts
|
|
12504
12303
|
/** 종료 코드 — 0 성공 · 1 실패 · 3 발행이 승인 대기(에이전트 토큰) */
|
|
@@ -12620,12 +12419,16 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
|
|
|
12620
12419
|
if (sha256Hex(tgz) !== source.archive.sha256) throw new CliError(cli_pull_archive_corrupt());
|
|
12621
12420
|
const files = await unpackArchive(tgz);
|
|
12622
12421
|
withGuideSkill(files);
|
|
12422
|
+
const manager = packageManagerOfFiles(files);
|
|
12623
12423
|
const collisions = caseCollisions(files.keys());
|
|
12624
12424
|
if (collisions.length > 0) throw new CliError(cli_pull_case_collisions({ groups: collisions.map((group) => group.join(" · ")).join(", ") }));
|
|
12625
12425
|
const diff = diffLocal(target, files);
|
|
12626
12426
|
if (existing.length > 0) {
|
|
12627
12427
|
const watched = diff.overwrite.filter((path) => path === "package.json" || path === "vite.config.ts");
|
|
12628
|
-
for (const path of watched) io.out(cli_pull_watched_changed({
|
|
12428
|
+
for (const path of watched) io.out(cli_pull_watched_changed({
|
|
12429
|
+
path,
|
|
12430
|
+
install: installCommand(manager)
|
|
12431
|
+
}));
|
|
12629
12432
|
if (diff.remove.length + diff.overwrite.length > 0) {
|
|
12630
12433
|
io.out(cli_pull_replace_summary({
|
|
12631
12434
|
remove: diff.remove.length,
|
|
@@ -12688,81 +12491,19 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
|
|
|
12688
12491
|
siteId: source.siteId
|
|
12689
12492
|
}));
|
|
12690
12493
|
if (source.editSessionOpen) io.out(cli_pull_edit_session_open());
|
|
12691
|
-
io.out(cli_pull_next_steps({
|
|
12494
|
+
io.out(cli_pull_next_steps({
|
|
12495
|
+
dir: pulledDir,
|
|
12496
|
+
install: installCommand(manager),
|
|
12497
|
+
dev: devCommand(manager)
|
|
12498
|
+
}));
|
|
12692
12499
|
return 0;
|
|
12693
12500
|
}
|
|
12694
12501
|
/**
|
|
12695
|
-
* `sayren init
|
|
12696
|
-
*
|
|
12697
|
-
* 토큰은 `storefront:rw`가 있어야 한다(로그인 토큰 `CLI_LOGIN_SCOPE`에 있다). 에이전트 토큰이어도 만들기는 승인 대상이 아니다
|
|
12698
|
-
* (쓰기 정책 표에 없다 — 오픈 준비 중으로 시작해 구매자 화면을 바꾸지 않는다).
|
|
12502
|
+
* `sayren init`(#101)은 템플릿을 골라 호스팅 사이트를 만들던 명령이다. 템플릿 사이트를 만들지 않으므로(#315 2차, 공개 API 템플릿 삭제)
|
|
12503
|
+
* 사이트를 만들지 않고 시작 경로(`create` → `link`)를 안내하고 끝낸다
|
|
12699
12504
|
*/
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
if (target && existsSync(target) && readdirSync(target).length > 0) throw new CliError(cli_init_dir_not_empty({ dir: relativeOf(io, target) }));
|
|
12703
|
-
if (!target && isPulledDir(io.cwd)) throw new CliError(cli_init_inside_pulled());
|
|
12704
|
-
const authDir = target && existsSync(target) ? target : io.cwd;
|
|
12705
|
-
const { auth, client } = await apiFor(flags, authDir, null, io);
|
|
12706
|
-
const storeId = auth.storeId ?? "<storeId>";
|
|
12707
|
-
const hosting = await client.storefront.get();
|
|
12708
|
-
if (hosting.site) {
|
|
12709
|
-
io.out(cli_init_site_url({ url: hosting.site.url }));
|
|
12710
|
-
throw new CliError(cli_init_site_exists({ storeId }));
|
|
12711
|
-
}
|
|
12712
|
-
if (!hosting.editingEnabled) throw new CliError(cli_pull_editing_disabled());
|
|
12713
|
-
const template = await pickTemplate(await client.storefront.listTemplates(), flags.template, io);
|
|
12714
|
-
io.out(cli_init_summary({
|
|
12715
|
-
name: template.name,
|
|
12716
|
-
templateId: template.templateId,
|
|
12717
|
-
version: template.version,
|
|
12718
|
-
storeId
|
|
12719
|
-
}));
|
|
12720
|
-
if (!flags.yes) {
|
|
12721
|
-
if (!io.confirm) throw new CliError(cli_init_yes_required());
|
|
12722
|
-
if (!await io.confirm(cli_init_confirm())) {
|
|
12723
|
-
io.out(cli_cancelled());
|
|
12724
|
-
return 1;
|
|
12725
|
-
}
|
|
12726
|
-
}
|
|
12727
|
-
let site;
|
|
12728
|
-
try {
|
|
12729
|
-
site = await client.storefront.create({ templateId: template.templateId });
|
|
12730
|
-
} catch (error) {
|
|
12731
|
-
const code = codeOf(error);
|
|
12732
|
-
if (code === "STOREFRONT_SITE_EXISTS") throw new CliError(cli_init_site_exists({ storeId }));
|
|
12733
|
-
if (code === "STOREFRONT_SLUG_TAKEN") throw new CliError(cli_init_slug_taken());
|
|
12734
|
-
throw error;
|
|
12735
|
-
}
|
|
12736
|
-
io.out(cli_init_created({ url: site.url }));
|
|
12737
|
-
try {
|
|
12738
|
-
return await receiveSource(client, auth, await client.storefront.getSource(), { dir: flags.dir }, io, authDir);
|
|
12739
|
-
} catch (error) {
|
|
12740
|
-
io.err(cli_init_pull_failed({ storeId }));
|
|
12741
|
-
throw error;
|
|
12742
|
-
}
|
|
12743
|
-
}
|
|
12744
|
-
/** `--template`이 있으면 그 템플릿, 없으면 대화형 선택. 비대화형이면 `--template`이 있어야 한다 */
|
|
12745
|
-
async function pickTemplate(templates, wanted, io) {
|
|
12746
|
-
if (templates.length === 0) throw new CliError(cli_init_no_templates());
|
|
12747
|
-
const ids = templates.map((t) => t.templateId).join(", ");
|
|
12748
|
-
if (wanted) {
|
|
12749
|
-
const found = templates.find((t) => t.templateId === wanted);
|
|
12750
|
-
if (!found) throw new CliError(cli_init_template_unknown({
|
|
12751
|
-
templateId: wanted,
|
|
12752
|
-
ids
|
|
12753
|
-
}));
|
|
12754
|
-
return found;
|
|
12755
|
-
}
|
|
12756
|
-
const labels = templates.map((t) => cli_init_template_option({
|
|
12757
|
-
templateId: t.templateId,
|
|
12758
|
-
name: t.name,
|
|
12759
|
-
version: t.version,
|
|
12760
|
-
description: t.description
|
|
12761
|
-
}));
|
|
12762
|
-
const picked = io.select ? await io.select(cli_init_template_prompt(), labels) : null;
|
|
12763
|
-
const index = picked ? labels.indexOf(picked) : -1;
|
|
12764
|
-
if (index < 0) throw new CliError(cli_init_template_required({ list: labels.map((label) => ` ${label}`).join("\n") }));
|
|
12765
|
-
return templates[index];
|
|
12505
|
+
function init() {
|
|
12506
|
+
throw new CliError(cli_init_retired());
|
|
12766
12507
|
}
|
|
12767
12508
|
/**
|
|
12768
12509
|
* `sayren link [폴더]`(#315 C6) — 폴더를 상점의 호스팅 사이트에 연결한다. 상점은 `--store` → 토큰 범위 → 대화형 선택이고, 사이트가 없으면
|
|
@@ -12774,7 +12515,11 @@ async function link(flags, io) {
|
|
|
12774
12515
|
if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
|
|
12775
12516
|
migrateLegacy(dir, io.out);
|
|
12776
12517
|
await linkDir(flags, dir, io);
|
|
12777
|
-
|
|
12518
|
+
const manager = packageManagerOfDir(dir);
|
|
12519
|
+
io.out(cli_link_next_steps({
|
|
12520
|
+
install: installCommand(manager),
|
|
12521
|
+
dev: devCommand(manager)
|
|
12522
|
+
}));
|
|
12778
12523
|
return 0;
|
|
12779
12524
|
}
|
|
12780
12525
|
/** link 본체 — `deploy`도 연결이 없으면 이것부터 부른다 */
|
|
@@ -13023,7 +12768,7 @@ async function localBuild(dir, skip, io) {
|
|
|
13023
12768
|
return;
|
|
13024
12769
|
}
|
|
13025
12770
|
if (plan.kind === "no-deps") {
|
|
13026
|
-
io.out(cli_deploy_build_local_no_deps());
|
|
12771
|
+
io.out(cli_deploy_build_local_no_deps({ install: installCommand(packageManagerOfDir(dir)) }));
|
|
13027
12772
|
return;
|
|
13028
12773
|
}
|
|
13029
12774
|
io.out(cli_deploy_build_local_running());
|
|
@@ -13372,9 +13117,10 @@ var StarterTransformError = class extends Error {
|
|
|
13372
13117
|
this.code = code;
|
|
13373
13118
|
}
|
|
13374
13119
|
};
|
|
13375
|
-
/** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결
|
|
13120
|
+
/** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결 정보·원본 저장소의 CI */
|
|
13376
13121
|
function isStarterLocalOnly(path) {
|
|
13377
13122
|
if (path === ".sayren" || path.startsWith(".sayren/")) return true;
|
|
13123
|
+
if (path === ".github" || path.startsWith(".github/")) return true;
|
|
13378
13124
|
if (path.includes("/")) return false;
|
|
13379
13125
|
return (path === ".env" || path.startsWith(".env.")) && path !== ".env.example";
|
|
13380
13126
|
}
|
|
@@ -13413,140 +13159,45 @@ function withPackageName(bytes, name, fields = {}) {
|
|
|
13413
13159
|
return new TextEncoder().encode(`${JSON.stringify(next, null, 2)}\n`);
|
|
13414
13160
|
}
|
|
13415
13161
|
|
|
13416
|
-
//#endregion
|
|
13417
|
-
//#region ../mcp/src/template-package.ts
|
|
13418
|
-
/** package.json의 기준 템플릿 표식 필드 — 원천은 이 함수다(api `template-marker.ts`가 같은 모양을 읽는다) */
|
|
13419
|
-
function withTemplateMarker(pkg, marker) {
|
|
13420
|
-
const current = pkg.sayren;
|
|
13421
|
-
pkg.sayren = {
|
|
13422
|
-
...current && typeof current === "object" && !Array.isArray(current) ? current : {},
|
|
13423
|
-
template: marker.id,
|
|
13424
|
-
templateVersion: marker.version
|
|
13425
|
-
};
|
|
13426
|
-
return pkg;
|
|
13427
|
-
}
|
|
13428
|
-
const DEP_FIELDS = [
|
|
13429
|
-
"dependencies",
|
|
13430
|
-
"devDependencies",
|
|
13431
|
-
"peerDependencies"
|
|
13432
|
-
];
|
|
13433
|
-
function renderTemplatePackageJson(raw, meta, options = {}) {
|
|
13434
|
-
const pkg = JSON.parse(raw);
|
|
13435
|
-
for (const field of DEP_FIELDS) {
|
|
13436
|
-
const deps = pkg[field];
|
|
13437
|
-
if (!deps) continue;
|
|
13438
|
-
for (const [name, spec] of Object.entries(deps)) deps[name] = resolveSpec(name, spec, meta);
|
|
13439
|
-
}
|
|
13440
|
-
if (options.name) pkg.name = options.name;
|
|
13441
|
-
const marker = options.template ?? meta.template;
|
|
13442
|
-
if (marker) withTemplateMarker(pkg, marker);
|
|
13443
|
-
return `${JSON.stringify(pkg, null, 2)}\n`;
|
|
13444
|
-
}
|
|
13445
|
-
function resolveSpec(name, spec, meta) {
|
|
13446
|
-
if (spec === "catalog:" || spec === "catalog:default") {
|
|
13447
|
-
const version$1 = meta.catalog[name];
|
|
13448
|
-
if (!version$1) throw new Error(mcp_template_catalog_version_missing({ name }));
|
|
13449
|
-
return version$1;
|
|
13450
|
-
}
|
|
13451
|
-
if (spec.startsWith("workspace:")) return workspaceRange(name, spec.slice(10), meta);
|
|
13452
|
-
return spec;
|
|
13453
|
-
}
|
|
13454
|
-
/**
|
|
13455
|
-
* `workspace:` 뒤 표기 → 셀러 프로젝트의 범위(#232). 템플릿은 SDK를 범위(`workspace:^0.35.0`)로 적고 `workspace:`만 뗀다 —
|
|
13456
|
-
* SDK patch가 올라도 풀어 쓴 package.json·첫 작업 트리가 그대로라 템플릿 버전을 올리지 않는다. 범위가 아닌 `*`·`^`·`~`만
|
|
13457
|
-
* 지금 워크스페이스 버전으로 푼다(`*`는 `^{버전}`)
|
|
13458
|
-
*/
|
|
13459
|
-
function workspaceRange(name, rest, meta) {
|
|
13460
|
-
if (rest !== "*" && rest !== "^" && rest !== "~" && rest !== "") return rest;
|
|
13461
|
-
const version$1 = meta.versions[name];
|
|
13462
|
-
if (!version$1) throw new Error(mcp_template_published_version_unknown({ name }));
|
|
13463
|
-
return `${rest === "~" ? "~" : "^"}${version$1}`;
|
|
13464
|
-
}
|
|
13465
|
-
/** pnpm-workspace.yaml의 최상위 `catalog:` 블록만 읽는다(` key: value`, 따옴표 허용) */
|
|
13466
|
-
function parseCatalog(yaml) {
|
|
13467
|
-
const out = {};
|
|
13468
|
-
let inCatalog = false;
|
|
13469
|
-
for (const line of yaml.split("\n")) {
|
|
13470
|
-
if (/^catalog:\s*$/.test(line)) {
|
|
13471
|
-
inCatalog = true;
|
|
13472
|
-
continue;
|
|
13473
|
-
}
|
|
13474
|
-
if (!inCatalog) continue;
|
|
13475
|
-
if (/^\S/.test(line)) break;
|
|
13476
|
-
const match = line.match(/^\s+["']?([^"':]+)["']?\s*:\s*["']?([^"'#\s]+)["']?/);
|
|
13477
|
-
if (match?.[1] && match[2]) out[match[1].trim()] = match[2];
|
|
13478
|
-
}
|
|
13479
|
-
return out;
|
|
13480
|
-
}
|
|
13481
|
-
function readWorkspaceVersions(packagesDir) {
|
|
13482
|
-
const out = {};
|
|
13483
|
-
for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
|
|
13484
|
-
if (!entry.isDirectory()) continue;
|
|
13485
|
-
const file = join(packagesDir, entry.name, "package.json");
|
|
13486
|
-
if (!existsSync(file)) continue;
|
|
13487
|
-
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
13488
|
-
if (pkg.name && pkg.version) out[pkg.name] = pkg.version;
|
|
13489
|
-
}
|
|
13490
|
-
return out;
|
|
13491
|
-
}
|
|
13492
|
-
|
|
13493
13162
|
//#endregion
|
|
13494
13163
|
//#region src/create-command.ts
|
|
13495
13164
|
/**
|
|
13496
13165
|
* `npx sayren create <이름> [--template <템플릿>]`(#271, #315 C7) — 시작 코드를 새 폴더에 쓴다. 파일만 만들고 상점을 묻지 않는다.
|
|
13497
13166
|
* 상점 연결은 `sayren link`(사이트·`.sayren/project.json`)와 `sayren env pull`(`.env`)이 한다. `--store-code`를 주면 그 값으로 `.env`만 쓴다.
|
|
13498
13167
|
*
|
|
13499
|
-
* 템플릿: 없으면
|
|
13500
|
-
*
|
|
13168
|
+
* 템플릿: 없으면 시작 코드 공개 저장소(`avarcorp/sayren-starter`)의 최신 릴리스 태그, `github:owner/repo[/경로][#ref]`는 그 저장소다.
|
|
13169
|
+
* 둘 다 codeload tarball로 받는다(CLI에 시작 코드를 묶지 않는다 — 네트워크가 필요하다). 그 밖의 `<id>`는 sayren 레지스트리 자리다
|
|
13170
|
+
* (마켓 전까지 늘 「템플릿을 찾을 수 없습니다」).
|
|
13171
|
+
*
|
|
13172
|
+
* 받은 소스는 변환 함수 하나(`starter-transform.ts`, 서버와 공유)를 지난다. 변환(`package.json` `name`·`private`, `_gitignore` → `.gitignore`,
|
|
13173
|
+
* 로컬 전용 파일 빼기)은 **고정 계약이다**: 이미 설치된 옛 CLI도 늘 최신 시작 코드를 받으므로 시작 코드 저장소는 이 변환만 믿고 쓰고,
|
|
13174
|
+
* CLI는 변환을 바꾸거나 새 전제를 더하지 않는다. 안내 스킬(`.claude/skills/sayren/SKILL.md`)은 시작 코드 저장소에 두지 않고 CLI가 써 넣는다.
|
|
13501
13175
|
*/
|
|
13502
|
-
/**
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
if (!found) throw new Error(`boilerplate not found: ${candidates.join(", ")}`);
|
|
13508
|
-
return found;
|
|
13509
|
-
}
|
|
13510
|
-
/** 싣지 않는 것 — 설치물·산출물. 점 파일은 에이전트 안내(`.claude/`·`.mcp.json`)와 `.gitignore`만(scripts/starter-bundle.mjs와 같다) */
|
|
13511
|
-
const SKIP_DIRS = new Set([
|
|
13512
|
-
"node_modules",
|
|
13513
|
-
"dist",
|
|
13514
|
-
".output",
|
|
13515
|
-
".tanstack",
|
|
13516
|
-
".nitro",
|
|
13517
|
-
".wrangler"
|
|
13518
|
-
]);
|
|
13519
|
-
const DOT_KEPT = new Set([
|
|
13520
|
-
".claude",
|
|
13521
|
-
".mcp.json",
|
|
13522
|
-
".gitignore"
|
|
13523
|
-
]);
|
|
13176
|
+
/** 기본 시작 코드 — 공개 저장소. 릴리스(태그 v*)는 그 저장소 Actions의 검사를 통과해야 생긴다 */
|
|
13177
|
+
const STARTER_REPO = {
|
|
13178
|
+
owner: "avarcorp",
|
|
13179
|
+
repo: "sayren-starter"
|
|
13180
|
+
};
|
|
13524
13181
|
/**
|
|
13525
|
-
*
|
|
13526
|
-
*
|
|
13182
|
+
* 기본 시작 코드의 최신 릴리스 태그 — `releases/latest`가 302로 가리키는 `/releases/tag/{태그}`에서 읽는다(GitHub API rate limit을 쓰지 않는다).
|
|
13183
|
+
* 릴리스가 없으면 GitHub은 `/releases`로 보내거나 404다
|
|
13527
13184
|
*/
|
|
13528
|
-
function
|
|
13529
|
-
const
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
};
|
|
13539
|
-
walk(root);
|
|
13540
|
-
const repo = join(root, "..", "..", "..");
|
|
13541
|
-
const pkg = files.get("package.json");
|
|
13542
|
-
if (pkg && existsSync(join(repo, "pnpm-workspace.yaml"))) {
|
|
13543
|
-
const meta = {
|
|
13544
|
-
catalog: parseCatalog(readFileSync(join(repo, "pnpm-workspace.yaml"), "utf8")),
|
|
13545
|
-
versions: readWorkspaceVersions(join(repo, "packages"))
|
|
13546
|
-
};
|
|
13547
|
-
files.set("package.json", Buffer.from(renderTemplatePackageJson(Buffer.from(pkg).toString("utf8"), meta)));
|
|
13185
|
+
async function latestStarterTag(io) {
|
|
13186
|
+
const url = `https://github.com/${STARTER_REPO.owner}/${STARTER_REPO.repo}/releases/latest`;
|
|
13187
|
+
let res;
|
|
13188
|
+
try {
|
|
13189
|
+
res = await io.fetch(url, {
|
|
13190
|
+
method: "HEAD",
|
|
13191
|
+
redirect: "manual"
|
|
13192
|
+
});
|
|
13193
|
+
} catch {
|
|
13194
|
+
throw new CliError(cli_create_starter_unreachable());
|
|
13548
13195
|
}
|
|
13549
|
-
|
|
13196
|
+
const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
13197
|
+
const tag = location ? /\/releases\/tag\/([^/?#]+)$/.exec(new URL(location, url).pathname)?.[1] : void 0;
|
|
13198
|
+
const decoded = tag ? decodeURIComponent(tag) : "";
|
|
13199
|
+
if (!GIT_REF.test(decoded) || decoded.includes("..")) throw new CliError(cli_create_starter_no_release({ status: String(res.status) }));
|
|
13200
|
+
return decoded;
|
|
13550
13201
|
}
|
|
13551
13202
|
const GITHUB_OWNER = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
|
|
13552
13203
|
const GITHUB_REPO = /^[A-Za-z0-9._-]{1,100}$/;
|
|
@@ -13555,7 +13206,7 @@ const GIT_REF = /^[A-Za-z0-9._/+-]{1,255}$/;
|
|
|
13555
13206
|
/** `--template` 값 → 받을 곳. 형식이 틀리면 api·네트워크를 부르기 전에 멈춘다 */
|
|
13556
13207
|
function parseTemplateSpec(value) {
|
|
13557
13208
|
const raw = value?.trim();
|
|
13558
|
-
if (!raw) return { kind: "
|
|
13209
|
+
if (!raw) return { kind: "starter" };
|
|
13559
13210
|
if (!raw.startsWith("github:")) return {
|
|
13560
13211
|
kind: "registry",
|
|
13561
13212
|
id: raw
|
|
@@ -13627,7 +13278,17 @@ async function create(flags, io) {
|
|
|
13627
13278
|
if (storeCode !== void 0 && !STORE_CODE_PATTERN.test(storeCode)) throw new CliError(cli_create_store_code_invalid({ storeCode }));
|
|
13628
13279
|
const spec = parseTemplateSpec(flags.template);
|
|
13629
13280
|
if (spec.kind === "registry") throw new CliError(cli_create_template_not_found({ template: spec.id }));
|
|
13630
|
-
|
|
13281
|
+
let source;
|
|
13282
|
+
if (spec.kind === "github") source = await githubTemplateFiles(spec, flags.template?.trim() ?? "", io);
|
|
13283
|
+
else {
|
|
13284
|
+
const ref = await latestStarterTag(io);
|
|
13285
|
+
source = await githubTemplateFiles({
|
|
13286
|
+
kind: "github",
|
|
13287
|
+
...STARTER_REPO,
|
|
13288
|
+
path: "",
|
|
13289
|
+
ref
|
|
13290
|
+
}, `github:${STARTER_REPO.owner}/${STARTER_REPO.repo}#${ref}`, io);
|
|
13291
|
+
}
|
|
13631
13292
|
let files;
|
|
13632
13293
|
try {
|
|
13633
13294
|
files = transformStarterFiles(source, { name });
|
|
@@ -13635,7 +13296,13 @@ async function create(flags, io) {
|
|
|
13635
13296
|
if (error instanceof StarterTransformError) throw new CliError(cli_create_template_no_package_json({ reason: error.message }));
|
|
13636
13297
|
throw error;
|
|
13637
13298
|
}
|
|
13299
|
+
files.set(GUIDE_SKILL_PATH, new TextEncoder().encode(GUIDE_SKILL));
|
|
13638
13300
|
writeTree(target, files);
|
|
13301
|
+
const manager = packageManagerOfFiles(files);
|
|
13302
|
+
const steps = {
|
|
13303
|
+
install: installCommand(manager),
|
|
13304
|
+
dev: devCommand(manager)
|
|
13305
|
+
};
|
|
13639
13306
|
if (storeCode) {
|
|
13640
13307
|
const { apiOrigin } = resolveApiOrigin(flags, target, null, io.env);
|
|
13641
13308
|
updateEnvFile(target, {
|
|
@@ -13645,11 +13312,13 @@ async function create(flags, io) {
|
|
|
13645
13312
|
io.out(cli_create_done_store_code({
|
|
13646
13313
|
dir: name,
|
|
13647
13314
|
count: files.size,
|
|
13648
|
-
storeCode
|
|
13315
|
+
storeCode,
|
|
13316
|
+
...steps
|
|
13649
13317
|
}));
|
|
13650
13318
|
} else io.out(cli_create_done({
|
|
13651
13319
|
dir: name,
|
|
13652
|
-
count: files.size
|
|
13320
|
+
count: files.size,
|
|
13321
|
+
...steps
|
|
13653
13322
|
}));
|
|
13654
13323
|
return 0;
|
|
13655
13324
|
}
|
|
@@ -13808,11 +13477,7 @@ async function main(argv, io) {
|
|
|
13808
13477
|
...common,
|
|
13809
13478
|
dir: str(parsed.flags.dir) ?? parsed.positional[1]
|
|
13810
13479
|
}, io);
|
|
13811
|
-
case "init": return init(
|
|
13812
|
-
...common,
|
|
13813
|
-
template: str(parsed.flags.template),
|
|
13814
|
-
yes: parsed.flags.yes === true
|
|
13815
|
-
}, io);
|
|
13480
|
+
case "init": return init();
|
|
13816
13481
|
case "pull": return pull({
|
|
13817
13482
|
...common,
|
|
13818
13483
|
project: str(parsed.flags.project),
|