sayren 0.14.0 → 0.15.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/dist/boilerplate/package.json +5 -5
- package/dist/index.mjs +817 -194
- package/package.json +4 -4
- package/dist/boilerplate/.boilerplate-meta.json +0 -44
package/dist/index.mjs
CHANGED
|
@@ -3633,13 +3633,13 @@ function isGitTracked(dir, name) {
|
|
|
3633
3633
|
}
|
|
3634
3634
|
/** 심볼릭 링크면 쓰지 않는다(링크를 따라 폴더 밖 파일을 덮지 않게). 권한은 600이다 */
|
|
3635
3635
|
function writePrivateFile(path, text) {
|
|
3636
|
-
let link = false;
|
|
3636
|
+
let link$1 = false;
|
|
3637
3637
|
try {
|
|
3638
|
-
link = lstatSync(path).isSymbolicLink();
|
|
3638
|
+
link$1 = lstatSync(path).isSymbolicLink();
|
|
3639
3639
|
} catch {
|
|
3640
|
-
link = false;
|
|
3640
|
+
link$1 = false;
|
|
3641
3641
|
}
|
|
3642
|
-
if (link) throw new McpJsonError(mcp_json_symlink_refused({ path }));
|
|
3642
|
+
if (link$1) throw new McpJsonError(mcp_json_symlink_refused({ path }));
|
|
3643
3643
|
writeFileSync(path, text, { mode: 384 });
|
|
3644
3644
|
try {
|
|
3645
3645
|
chmodSync(path, 384);
|
|
@@ -5511,24 +5511,6 @@ const cli_checks_lock_drift = ((inputs, options = {}) => {
|
|
|
5511
5511
|
return ko_cli_checks_lock_drift(inputs);
|
|
5512
5512
|
});
|
|
5513
5513
|
|
|
5514
|
-
//#endregion
|
|
5515
|
-
//#region src/paraglide/messages/cli_checks_lock_missing.js
|
|
5516
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5517
|
-
/** @typedef {{}} Cli_Checks_Lock_MissingInputs */
|
|
5518
|
-
const ko_cli_checks_lock_missing = () => {
|
|
5519
|
-
return `pnpm-lock.yaml이 없습니다. 호스팅 빌드의 설치가 실패할 수 있습니다`;
|
|
5520
|
-
};
|
|
5521
|
-
/**
|
|
5522
|
-
|
|
5523
|
-
* @param {Cli_Checks_Lock_MissingInputs} inputs
|
|
5524
|
-
* @param {{ locale?: "ko" }} options
|
|
5525
|
-
* @returns {LocalizedString}
|
|
5526
|
-
*/
|
|
5527
|
-
const cli_checks_lock_missing = ((inputs = {}, options = {}) => {
|
|
5528
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5529
|
-
return ko_cli_checks_lock_missing(inputs);
|
|
5530
|
-
});
|
|
5531
|
-
|
|
5532
5514
|
//#endregion
|
|
5533
5515
|
//#region src/paraglide/messages/cli_checks_summary.js
|
|
5534
5516
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -5586,12 +5568,13 @@ const cli_create_dir_not_empty = ((inputs, options = {}) => {
|
|
|
5586
5568
|
//#endregion
|
|
5587
5569
|
//#region src/paraglide/messages/cli_create_done.js
|
|
5588
5570
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5589
|
-
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown
|
|
5571
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown> }} Cli_Create_DoneInputs */
|
|
5590
5572
|
const ko_cli_create_done = (i) => {
|
|
5591
|
-
return
|
|
5573
|
+
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개)
|
|
5592
5574
|
다음:
|
|
5593
5575
|
cd ${i?.dir}
|
|
5594
5576
|
npm install
|
|
5577
|
+
npx sayren link # 상점에 연결하고 .env에 상점 값을 씁니다
|
|
5595
5578
|
npm run dev`;
|
|
5596
5579
|
};
|
|
5597
5580
|
/**
|
|
@@ -5606,39 +5589,44 @@ const cli_create_done = ((inputs, options = {}) => {
|
|
|
5606
5589
|
});
|
|
5607
5590
|
|
|
5608
5591
|
//#endregion
|
|
5609
|
-
//#region src/paraglide/messages/
|
|
5592
|
+
//#region src/paraglide/messages/cli_create_done_store_code.js
|
|
5610
5593
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5611
|
-
/** @typedef {{
|
|
5612
|
-
const
|
|
5613
|
-
return
|
|
5594
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
|
|
5595
|
+
const ko_cli_create_done_store_code = (i) => {
|
|
5596
|
+
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개, .env에 상점 ${i?.storeCode} 연결 값)
|
|
5597
|
+
다음:
|
|
5598
|
+
cd ${i?.dir}
|
|
5599
|
+
npm install
|
|
5600
|
+
npm run dev
|
|
5601
|
+
npx sayren deploy # 처음이면 상점 사이트에 연결한 뒤 배포합니다`;
|
|
5614
5602
|
};
|
|
5615
5603
|
/**
|
|
5616
5604
|
|
|
5617
|
-
* @param {
|
|
5605
|
+
* @param {Cli_Create_Done_Store_CodeInputs} inputs
|
|
5618
5606
|
* @param {{ locale?: "ko" }} options
|
|
5619
5607
|
* @returns {LocalizedString}
|
|
5620
5608
|
*/
|
|
5621
|
-
const
|
|
5609
|
+
const cli_create_done_store_code = ((inputs, options = {}) => {
|
|
5622
5610
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5623
|
-
return
|
|
5611
|
+
return ko_cli_create_done_store_code(inputs);
|
|
5624
5612
|
});
|
|
5625
5613
|
|
|
5626
5614
|
//#endregion
|
|
5627
|
-
//#region src/paraglide/messages/
|
|
5615
|
+
//#region src/paraglide/messages/cli_create_name_invalid.js
|
|
5628
5616
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5629
|
-
/** @typedef {{}}
|
|
5630
|
-
const
|
|
5631
|
-
return
|
|
5617
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_Create_Name_InvalidInputs */
|
|
5618
|
+
const ko_cli_create_name_invalid = (i) => {
|
|
5619
|
+
return `폴더 이름은 영문 소문자·숫자·.·_·-로 쓰고 영문 소문자나 숫자로 시작합니다: ${i?.name}`;
|
|
5632
5620
|
};
|
|
5633
5621
|
/**
|
|
5634
5622
|
|
|
5635
|
-
* @param {
|
|
5623
|
+
* @param {Cli_Create_Name_InvalidInputs} inputs
|
|
5636
5624
|
* @param {{ locale?: "ko" }} options
|
|
5637
5625
|
* @returns {LocalizedString}
|
|
5638
5626
|
*/
|
|
5639
|
-
const
|
|
5627
|
+
const cli_create_name_invalid = ((inputs, options = {}) => {
|
|
5640
5628
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5641
|
-
return
|
|
5629
|
+
return ko_cli_create_name_invalid(inputs);
|
|
5642
5630
|
});
|
|
5643
5631
|
|
|
5644
5632
|
//#endregion
|
|
@@ -5660,21 +5648,165 @@ const cli_create_store_code_invalid = ((inputs, options = {}) => {
|
|
|
5660
5648
|
});
|
|
5661
5649
|
|
|
5662
5650
|
//#endregion
|
|
5663
|
-
//#region src/paraglide/messages/
|
|
5651
|
+
//#region src/paraglide/messages/cli_create_template_download_failed.js
|
|
5652
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5653
|
+
/** @typedef {{ template: NonNullable<unknown>, status: NonNullable<unknown> }} Cli_Create_Template_Download_FailedInputs */
|
|
5654
|
+
const ko_cli_create_template_download_failed = (i) => {
|
|
5655
|
+
return `템플릿을 받지 못했습니다: ${i?.template} (HTTP ${i?.status})`;
|
|
5656
|
+
};
|
|
5657
|
+
/**
|
|
5658
|
+
|
|
5659
|
+
* @param {Cli_Create_Template_Download_FailedInputs} inputs
|
|
5660
|
+
* @param {{ locale?: "ko" }} options
|
|
5661
|
+
* @returns {LocalizedString}
|
|
5662
|
+
*/
|
|
5663
|
+
const cli_create_template_download_failed = ((inputs, options = {}) => {
|
|
5664
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5665
|
+
return ko_cli_create_template_download_failed(inputs);
|
|
5666
|
+
});
|
|
5667
|
+
|
|
5668
|
+
//#endregion
|
|
5669
|
+
//#region src/paraglide/messages/cli_create_template_github_not_found.js
|
|
5670
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5671
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Github_Not_FoundInputs */
|
|
5672
|
+
const ko_cli_create_template_github_not_found = (i) => {
|
|
5673
|
+
return `GitHub에서 템플릿을 찾을 수 없습니다: ${i?.template}. 공개 저장소·ref를 확인하십시오`;
|
|
5674
|
+
};
|
|
5675
|
+
/**
|
|
5676
|
+
|
|
5677
|
+
* @param {Cli_Create_Template_Github_Not_FoundInputs} inputs
|
|
5678
|
+
* @param {{ locale?: "ko" }} options
|
|
5679
|
+
* @returns {LocalizedString}
|
|
5680
|
+
*/
|
|
5681
|
+
const cli_create_template_github_not_found = ((inputs, options = {}) => {
|
|
5682
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5683
|
+
return ko_cli_create_template_github_not_found(inputs);
|
|
5684
|
+
});
|
|
5685
|
+
|
|
5686
|
+
//#endregion
|
|
5687
|
+
//#region src/paraglide/messages/cli_create_template_invalid.js
|
|
5664
5688
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5665
|
-
/** @typedef {{}}
|
|
5666
|
-
const
|
|
5667
|
-
return
|
|
5689
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_InvalidInputs */
|
|
5690
|
+
const ko_cli_create_template_invalid = (i) => {
|
|
5691
|
+
return `템플릿 형식이 아닙니다: ${i?.template}. github:owner/repo[/경로][#ref] 또는 템플릿 id를 주십시오`;
|
|
5668
5692
|
};
|
|
5669
5693
|
/**
|
|
5670
5694
|
|
|
5671
|
-
* @param {
|
|
5695
|
+
* @param {Cli_Create_Template_InvalidInputs} inputs
|
|
5672
5696
|
* @param {{ locale?: "ko" }} options
|
|
5673
5697
|
* @returns {LocalizedString}
|
|
5674
5698
|
*/
|
|
5675
|
-
const
|
|
5699
|
+
const cli_create_template_invalid = ((inputs, options = {}) => {
|
|
5676
5700
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5677
|
-
return
|
|
5701
|
+
return ko_cli_create_template_invalid(inputs);
|
|
5702
|
+
});
|
|
5703
|
+
|
|
5704
|
+
//#endregion
|
|
5705
|
+
//#region src/paraglide/messages/cli_create_template_no_package_json.js
|
|
5706
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5707
|
+
/** @typedef {{ reason: NonNullable<unknown> }} Cli_Create_Template_No_Package_JsonInputs */
|
|
5708
|
+
const ko_cli_create_template_no_package_json = (i) => {
|
|
5709
|
+
return `시작 코드로 쓸 수 없는 템플릿입니다(${i?.reason})`;
|
|
5710
|
+
};
|
|
5711
|
+
/**
|
|
5712
|
+
|
|
5713
|
+
* @param {Cli_Create_Template_No_Package_JsonInputs} inputs
|
|
5714
|
+
* @param {{ locale?: "ko" }} options
|
|
5715
|
+
* @returns {LocalizedString}
|
|
5716
|
+
*/
|
|
5717
|
+
const cli_create_template_no_package_json = ((inputs, options = {}) => {
|
|
5718
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5719
|
+
return ko_cli_create_template_no_package_json(inputs);
|
|
5720
|
+
});
|
|
5721
|
+
|
|
5722
|
+
//#endregion
|
|
5723
|
+
//#region src/paraglide/messages/cli_create_template_not_found.js
|
|
5724
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5725
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Not_FoundInputs */
|
|
5726
|
+
const ko_cli_create_template_not_found = (i) => {
|
|
5727
|
+
return `템플릿을 찾을 수 없습니다: ${i?.template}`;
|
|
5728
|
+
};
|
|
5729
|
+
/**
|
|
5730
|
+
|
|
5731
|
+
* @param {Cli_Create_Template_Not_FoundInputs} inputs
|
|
5732
|
+
* @param {{ locale?: "ko" }} options
|
|
5733
|
+
* @returns {LocalizedString}
|
|
5734
|
+
*/
|
|
5735
|
+
const cli_create_template_not_found = ((inputs, options = {}) => {
|
|
5736
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5737
|
+
return ko_cli_create_template_not_found(inputs);
|
|
5738
|
+
});
|
|
5739
|
+
|
|
5740
|
+
//#endregion
|
|
5741
|
+
//#region src/paraglide/messages/cli_create_template_path_empty.js
|
|
5742
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5743
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Path_EmptyInputs */
|
|
5744
|
+
const ko_cli_create_template_path_empty = (i) => {
|
|
5745
|
+
return `템플릿 경로에 파일이 없습니다: ${i?.template}`;
|
|
5746
|
+
};
|
|
5747
|
+
/**
|
|
5748
|
+
|
|
5749
|
+
* @param {Cli_Create_Template_Path_EmptyInputs} inputs
|
|
5750
|
+
* @param {{ locale?: "ko" }} options
|
|
5751
|
+
* @returns {LocalizedString}
|
|
5752
|
+
*/
|
|
5753
|
+
const cli_create_template_path_empty = ((inputs, options = {}) => {
|
|
5754
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5755
|
+
return ko_cli_create_template_path_empty(inputs);
|
|
5756
|
+
});
|
|
5757
|
+
|
|
5758
|
+
//#endregion
|
|
5759
|
+
//#region src/paraglide/messages/cli_create_template_too_large.js
|
|
5760
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5761
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Too_LargeInputs */
|
|
5762
|
+
const ko_cli_create_template_too_large = (i) => {
|
|
5763
|
+
return `템플릿 저장소가 너무 커서 받지 않았습니다: ${i?.template}`;
|
|
5764
|
+
};
|
|
5765
|
+
/**
|
|
5766
|
+
|
|
5767
|
+
* @param {Cli_Create_Template_Too_LargeInputs} inputs
|
|
5768
|
+
* @param {{ locale?: "ko" }} options
|
|
5769
|
+
* @returns {LocalizedString}
|
|
5770
|
+
*/
|
|
5771
|
+
const cli_create_template_too_large = ((inputs, options = {}) => {
|
|
5772
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5773
|
+
return ko_cli_create_template_too_large(inputs);
|
|
5774
|
+
});
|
|
5775
|
+
|
|
5776
|
+
//#endregion
|
|
5777
|
+
//#region src/paraglide/messages/cli_create_template_unreachable.js
|
|
5778
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5779
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_UnreachableInputs */
|
|
5780
|
+
const ko_cli_create_template_unreachable = (i) => {
|
|
5781
|
+
return `템플릿을 받지 못했습니다: ${i?.template}. 네트워크 연결을 확인하십시오`;
|
|
5782
|
+
};
|
|
5783
|
+
/**
|
|
5784
|
+
|
|
5785
|
+
* @param {Cli_Create_Template_UnreachableInputs} inputs
|
|
5786
|
+
* @param {{ locale?: "ko" }} options
|
|
5787
|
+
* @returns {LocalizedString}
|
|
5788
|
+
*/
|
|
5789
|
+
const cli_create_template_unreachable = ((inputs, options = {}) => {
|
|
5790
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5791
|
+
return ko_cli_create_template_unreachable(inputs);
|
|
5792
|
+
});
|
|
5793
|
+
|
|
5794
|
+
//#endregion
|
|
5795
|
+
//#region src/paraglide/messages/cli_create_template_unreadable.js
|
|
5796
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5797
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_UnreadableInputs */
|
|
5798
|
+
const ko_cli_create_template_unreadable = (i) => {
|
|
5799
|
+
return `템플릿 압축 파일을 읽지 못했습니다: ${i?.template}`;
|
|
5800
|
+
};
|
|
5801
|
+
/**
|
|
5802
|
+
|
|
5803
|
+
* @param {Cli_Create_Template_UnreadableInputs} inputs
|
|
5804
|
+
* @param {{ locale?: "ko" }} options
|
|
5805
|
+
* @returns {LocalizedString}
|
|
5806
|
+
*/
|
|
5807
|
+
const cli_create_template_unreadable = ((inputs, options = {}) => {
|
|
5808
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5809
|
+
return ko_cli_create_template_unreadable(inputs);
|
|
5678
5810
|
});
|
|
5679
5811
|
|
|
5680
5812
|
//#endregion
|
|
@@ -5682,7 +5814,7 @@ const cli_create_store_code_required = ((inputs = {}, options = {}) => {
|
|
|
5682
5814
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5683
5815
|
/** @typedef {{}} Cli_Create_UsageInputs */
|
|
5684
5816
|
const ko_cli_create_usage = () => {
|
|
5685
|
-
return `폴더 이름을 주십시오: npx sayren create <이름> [--
|
|
5817
|
+
return `폴더 이름을 주십시오: npx sayren create <이름> [--template <템플릿>]`;
|
|
5686
5818
|
};
|
|
5687
5819
|
/**
|
|
5688
5820
|
|
|
@@ -6111,40 +6243,40 @@ const cli_deploy_force_yes_required = ((inputs = {}, options = {}) => {
|
|
|
6111
6243
|
});
|
|
6112
6244
|
|
|
6113
6245
|
//#endregion
|
|
6114
|
-
//#region src/paraglide/messages/
|
|
6246
|
+
//#region src/paraglide/messages/cli_deploy_linking.js
|
|
6115
6247
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6116
|
-
/** @typedef {{}}
|
|
6117
|
-
const
|
|
6118
|
-
return
|
|
6119
|
-
· 병합된 트리를 이 폴더에 받으려면: npx sayren pull --force`;
|
|
6248
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Deploy_LinkingInputs */
|
|
6249
|
+
const ko_cli_deploy_linking = (i) => {
|
|
6250
|
+
return `${i?.dir}는 아직 상점에 연결되지 않았습니다 — 먼저 연결합니다(npx sayren link)`;
|
|
6120
6251
|
};
|
|
6121
6252
|
/**
|
|
6122
6253
|
|
|
6123
|
-
* @param {
|
|
6254
|
+
* @param {Cli_Deploy_LinkingInputs} inputs
|
|
6124
6255
|
* @param {{ locale?: "ko" }} options
|
|
6125
6256
|
* @returns {LocalizedString}
|
|
6126
6257
|
*/
|
|
6127
|
-
const
|
|
6258
|
+
const cli_deploy_linking = ((inputs, options = {}) => {
|
|
6128
6259
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6129
|
-
return
|
|
6260
|
+
return ko_cli_deploy_linking(inputs);
|
|
6130
6261
|
});
|
|
6131
6262
|
|
|
6132
6263
|
//#endregion
|
|
6133
|
-
//#region src/paraglide/messages/
|
|
6264
|
+
//#region src/paraglide/messages/cli_deploy_merged.js
|
|
6134
6265
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6135
|
-
/** @typedef {{
|
|
6136
|
-
const
|
|
6137
|
-
return
|
|
6266
|
+
/** @typedef {{}} Cli_Deploy_MergedInputs */
|
|
6267
|
+
const ko_cli_deploy_merged = () => {
|
|
6268
|
+
return `pull 이후 원격 작업 트리가 바뀌어(상점 플랫폼·AI 편집) 서버가 병합했습니다. 원격 변경은 그대로 남습니다.
|
|
6269
|
+
· 병합된 트리를 이 폴더에 받으려면: npx sayren pull --force`;
|
|
6138
6270
|
};
|
|
6139
6271
|
/**
|
|
6140
6272
|
|
|
6141
|
-
* @param {
|
|
6273
|
+
* @param {Cli_Deploy_MergedInputs} inputs
|
|
6142
6274
|
* @param {{ locale?: "ko" }} options
|
|
6143
6275
|
* @returns {LocalizedString}
|
|
6144
6276
|
*/
|
|
6145
|
-
const
|
|
6277
|
+
const cli_deploy_merged = ((inputs = {}, options = {}) => {
|
|
6146
6278
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6147
|
-
return
|
|
6279
|
+
return ko_cli_deploy_merged(inputs);
|
|
6148
6280
|
});
|
|
6149
6281
|
|
|
6150
6282
|
//#endregion
|
|
@@ -6489,6 +6621,78 @@ const cli_edit_session_open = ((inputs = {}, options = {}) => {
|
|
|
6489
6621
|
return ko_cli_edit_session_open(inputs);
|
|
6490
6622
|
});
|
|
6491
6623
|
|
|
6624
|
+
//#endregion
|
|
6625
|
+
//#region src/paraglide/messages/cli_env_not_linked.js
|
|
6626
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6627
|
+
/** @typedef {{ file: NonNullable<unknown> }} Cli_Env_Not_LinkedInputs */
|
|
6628
|
+
const ko_cli_env_not_linked = (i) => {
|
|
6629
|
+
return `이 폴더는 상점에 연결되지 않았습니다(${i?.file} 없음). npx sayren link로 먼저 연결하십시오`;
|
|
6630
|
+
};
|
|
6631
|
+
/**
|
|
6632
|
+
|
|
6633
|
+
* @param {Cli_Env_Not_LinkedInputs} inputs
|
|
6634
|
+
* @param {{ locale?: "ko" }} options
|
|
6635
|
+
* @returns {LocalizedString}
|
|
6636
|
+
*/
|
|
6637
|
+
const cli_env_not_linked = ((inputs, options = {}) => {
|
|
6638
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6639
|
+
return ko_cli_env_not_linked(inputs);
|
|
6640
|
+
});
|
|
6641
|
+
|
|
6642
|
+
//#endregion
|
|
6643
|
+
//#region src/paraglide/messages/cli_env_pulled.js
|
|
6644
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6645
|
+
/** @typedef {{ file: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Env_PulledInputs */
|
|
6646
|
+
const ko_cli_env_pulled = (i) => {
|
|
6647
|
+
return `${i?.file}에 상점 ${i?.storeCode} 연결 값(SAYREN_API_URL·SAYREN_STORE_CODE)을 썼습니다`;
|
|
6648
|
+
};
|
|
6649
|
+
/**
|
|
6650
|
+
|
|
6651
|
+
* @param {Cli_Env_PulledInputs} inputs
|
|
6652
|
+
* @param {{ locale?: "ko" }} options
|
|
6653
|
+
* @returns {LocalizedString}
|
|
6654
|
+
*/
|
|
6655
|
+
const cli_env_pulled = ((inputs, options = {}) => {
|
|
6656
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6657
|
+
return ko_cli_env_pulled(inputs);
|
|
6658
|
+
});
|
|
6659
|
+
|
|
6660
|
+
//#endregion
|
|
6661
|
+
//#region src/paraglide/messages/cli_env_store_code_unknown.js
|
|
6662
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6663
|
+
/** @typedef {{}} Cli_Env_Store_Code_UnknownInputs */
|
|
6664
|
+
const ko_cli_env_store_code_unknown = () => {
|
|
6665
|
+
return `상점 코드를 읽지 못했습니다. 로그인(npx sayren login)을 다시 하거나 토큰에 stores:read 권한이 있는지 확인하십시오`;
|
|
6666
|
+
};
|
|
6667
|
+
/**
|
|
6668
|
+
|
|
6669
|
+
* @param {Cli_Env_Store_Code_UnknownInputs} inputs
|
|
6670
|
+
* @param {{ locale?: "ko" }} options
|
|
6671
|
+
* @returns {LocalizedString}
|
|
6672
|
+
*/
|
|
6673
|
+
const cli_env_store_code_unknown = ((inputs = {}, options = {}) => {
|
|
6674
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6675
|
+
return ko_cli_env_store_code_unknown(inputs);
|
|
6676
|
+
});
|
|
6677
|
+
|
|
6678
|
+
//#endregion
|
|
6679
|
+
//#region src/paraglide/messages/cli_env_usage.js
|
|
6680
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6681
|
+
/** @typedef {{}} Cli_Env_UsageInputs */
|
|
6682
|
+
const ko_cli_env_usage = () => {
|
|
6683
|
+
return `사용법: npx sayren env pull [--dir <폴더>]`;
|
|
6684
|
+
};
|
|
6685
|
+
/**
|
|
6686
|
+
|
|
6687
|
+
* @param {Cli_Env_UsageInputs} inputs
|
|
6688
|
+
* @param {{ locale?: "ko" }} options
|
|
6689
|
+
* @returns {LocalizedString}
|
|
6690
|
+
*/
|
|
6691
|
+
const cli_env_usage = ((inputs = {}, options = {}) => {
|
|
6692
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6693
|
+
return ko_cli_env_usage(inputs);
|
|
6694
|
+
});
|
|
6695
|
+
|
|
6492
6696
|
//#endregion
|
|
6493
6697
|
//#region src/paraglide/messages/cli_error_api_unreachable.js
|
|
6494
6698
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -7030,6 +7234,135 @@ const cli_legacy_rc_tracked = ((inputs, options = {}) => {
|
|
|
7030
7234
|
return ko_cli_legacy_rc_tracked(inputs);
|
|
7031
7235
|
});
|
|
7032
7236
|
|
|
7237
|
+
//#endregion
|
|
7238
|
+
//#region src/paraglide/messages/cli_link_dir_missing.js
|
|
7239
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7240
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Link_Dir_MissingInputs */
|
|
7241
|
+
const ko_cli_link_dir_missing = (i) => {
|
|
7242
|
+
return `폴더가 없습니다: ${i?.dir}`;
|
|
7243
|
+
};
|
|
7244
|
+
/**
|
|
7245
|
+
|
|
7246
|
+
* @param {Cli_Link_Dir_MissingInputs} inputs
|
|
7247
|
+
* @param {{ locale?: "ko" }} options
|
|
7248
|
+
* @returns {LocalizedString}
|
|
7249
|
+
*/
|
|
7250
|
+
const cli_link_dir_missing = ((inputs, options = {}) => {
|
|
7251
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7252
|
+
return ko_cli_link_dir_missing(inputs);
|
|
7253
|
+
});
|
|
7254
|
+
|
|
7255
|
+
//#endregion
|
|
7256
|
+
//#region src/paraglide/messages/cli_link_done.js
|
|
7257
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7258
|
+
/** @typedef {{ storeId: NonNullable<unknown>, url: NonNullable<unknown>, file: NonNullable<unknown>, storeCode: NonNullable<unknown>, envFile: NonNullable<unknown> }} Cli_Link_DoneInputs */
|
|
7259
|
+
const ko_cli_link_done = (i) => {
|
|
7260
|
+
return `연결했습니다 — 상점 ${i?.storeId}, 사이트 ${i?.url}. 연결 정보는 ${i?.file}, 상점 ${i?.storeCode} 연결 값은 ${i?.envFile}에 두었습니다`;
|
|
7261
|
+
};
|
|
7262
|
+
/**
|
|
7263
|
+
|
|
7264
|
+
* @param {Cli_Link_DoneInputs} inputs
|
|
7265
|
+
* @param {{ locale?: "ko" }} options
|
|
7266
|
+
* @returns {LocalizedString}
|
|
7267
|
+
*/
|
|
7268
|
+
const cli_link_done = ((inputs, options = {}) => {
|
|
7269
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7270
|
+
return ko_cli_link_done(inputs);
|
|
7271
|
+
});
|
|
7272
|
+
|
|
7273
|
+
//#endregion
|
|
7274
|
+
//#region src/paraglide/messages/cli_link_editing_disabled.js
|
|
7275
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7276
|
+
/** @typedef {{}} Cli_Link_Editing_DisabledInputs */
|
|
7277
|
+
const ko_cli_link_editing_disabled = () => {
|
|
7278
|
+
return `지금은 스토어프론트를 연결할 수 없습니다(편집 기능이 꺼져 있습니다)`;
|
|
7279
|
+
};
|
|
7280
|
+
/**
|
|
7281
|
+
|
|
7282
|
+
* @param {Cli_Link_Editing_DisabledInputs} inputs
|
|
7283
|
+
* @param {{ locale?: "ko" }} options
|
|
7284
|
+
* @returns {LocalizedString}
|
|
7285
|
+
*/
|
|
7286
|
+
const cli_link_editing_disabled = ((inputs = {}, options = {}) => {
|
|
7287
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7288
|
+
return ko_cli_link_editing_disabled(inputs);
|
|
7289
|
+
});
|
|
7290
|
+
|
|
7291
|
+
//#endregion
|
|
7292
|
+
//#region src/paraglide/messages/cli_link_gitignore.js
|
|
7293
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7294
|
+
/** @typedef {{ lines: NonNullable<unknown> }} Cli_Link_GitignoreInputs */
|
|
7295
|
+
const ko_cli_link_gitignore = (i) => {
|
|
7296
|
+
return `.gitignore에 ${i?.lines}를 더했습니다(배포에 올리지 않습니다)`;
|
|
7297
|
+
};
|
|
7298
|
+
/**
|
|
7299
|
+
|
|
7300
|
+
* @param {Cli_Link_GitignoreInputs} inputs
|
|
7301
|
+
* @param {{ locale?: "ko" }} options
|
|
7302
|
+
* @returns {LocalizedString}
|
|
7303
|
+
*/
|
|
7304
|
+
const cli_link_gitignore = ((inputs, options = {}) => {
|
|
7305
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7306
|
+
return ko_cli_link_gitignore(inputs);
|
|
7307
|
+
});
|
|
7308
|
+
|
|
7309
|
+
//#endregion
|
|
7310
|
+
//#region src/paraglide/messages/cli_link_next_steps.js
|
|
7311
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7312
|
+
/** @typedef {{}} Cli_Link_Next_StepsInputs */
|
|
7313
|
+
const ko_cli_link_next_steps = () => {
|
|
7314
|
+
return `다음:
|
|
7315
|
+
npm install
|
|
7316
|
+
npm run dev # http://localhost:4010 — 운영 상점 데이터로 동작합니다
|
|
7317
|
+
npx sayren deploy # 빌드 → 버전 → 발행`;
|
|
7318
|
+
};
|
|
7319
|
+
/**
|
|
7320
|
+
|
|
7321
|
+
* @param {Cli_Link_Next_StepsInputs} inputs
|
|
7322
|
+
* @param {{ locale?: "ko" }} options
|
|
7323
|
+
* @returns {LocalizedString}
|
|
7324
|
+
*/
|
|
7325
|
+
const cli_link_next_steps = ((inputs = {}, options = {}) => {
|
|
7326
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7327
|
+
return ko_cli_link_next_steps(inputs);
|
|
7328
|
+
});
|
|
7329
|
+
|
|
7330
|
+
//#endregion
|
|
7331
|
+
//#region src/paraglide/messages/cli_link_site_created.js
|
|
7332
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7333
|
+
/** @typedef {{ url: NonNullable<unknown> }} Cli_Link_Site_CreatedInputs */
|
|
7334
|
+
const ko_cli_link_site_created = (i) => {
|
|
7335
|
+
return `호스팅 사이트를 만들었습니다 — ${i?.url} (오픈 준비 중, 첫 배포가 성공하면 발행합니다)`;
|
|
7336
|
+
};
|
|
7337
|
+
/**
|
|
7338
|
+
|
|
7339
|
+
* @param {Cli_Link_Site_CreatedInputs} inputs
|
|
7340
|
+
* @param {{ locale?: "ko" }} options
|
|
7341
|
+
* @returns {LocalizedString}
|
|
7342
|
+
*/
|
|
7343
|
+
const cli_link_site_created = ((inputs, options = {}) => {
|
|
7344
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7345
|
+
return ko_cli_link_site_created(inputs);
|
|
7346
|
+
});
|
|
7347
|
+
|
|
7348
|
+
//#endregion
|
|
7349
|
+
//#region src/paraglide/messages/cli_link_slug_taken.js
|
|
7350
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7351
|
+
/** @typedef {{}} Cli_Link_Slug_TakenInputs */
|
|
7352
|
+
const ko_cli_link_slug_taken = () => {
|
|
7353
|
+
return `상점 코드와 같은 주소를 다른 사이트가 쓰고 있어 사이트를 만들지 못했습니다. 상점 플랫폼 › 스토어프론트에서 다른 주소로 만든 뒤 npx sayren link로 다시 연결하십시오`;
|
|
7354
|
+
};
|
|
7355
|
+
/**
|
|
7356
|
+
|
|
7357
|
+
* @param {Cli_Link_Slug_TakenInputs} inputs
|
|
7358
|
+
* @param {{ locale?: "ko" }} options
|
|
7359
|
+
* @returns {LocalizedString}
|
|
7360
|
+
*/
|
|
7361
|
+
const cli_link_slug_taken = ((inputs = {}, options = {}) => {
|
|
7362
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7363
|
+
return ko_cli_link_slug_taken(inputs);
|
|
7364
|
+
});
|
|
7365
|
+
|
|
7033
7366
|
//#endregion
|
|
7034
7367
|
//#region src/paraglide/messages/cli_login_browser_prompt.js
|
|
7035
7368
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -8413,7 +8746,7 @@ const cli_state_invalid = ((inputs, options = {}) => {
|
|
|
8413
8746
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8414
8747
|
/** @typedef {{ name: NonNullable<unknown> }} Cli_State_Missing_FieldsInputs */
|
|
8415
8748
|
const ko_cli_state_missing_fields = (i) => {
|
|
8416
|
-
return `${i?.name}에 storeId·siteId·baseRef가 없습니다. npx sayren
|
|
8749
|
+
return `${i?.name}에 storeId·siteId·baseRef가 없습니다. npx sayren link로 다시 연결하십시오`;
|
|
8417
8750
|
};
|
|
8418
8751
|
/**
|
|
8419
8752
|
|
|
@@ -8534,6 +8867,24 @@ const cli_status_folder_base = ((inputs, options = {}) => {
|
|
|
8534
8867
|
return ko_cli_status_folder_base(inputs);
|
|
8535
8868
|
});
|
|
8536
8869
|
|
|
8870
|
+
//#endregion
|
|
8871
|
+
//#region src/paraglide/messages/cli_status_folder_linked_only.js
|
|
8872
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8873
|
+
/** @typedef {{}} Cli_Status_Folder_Linked_OnlyInputs */
|
|
8874
|
+
const ko_cli_status_folder_linked_only = () => {
|
|
8875
|
+
return `이 폴더: 연결만 했습니다(아직 올리지 않음 — 다음 deploy가 교체합니다)`;
|
|
8876
|
+
};
|
|
8877
|
+
/**
|
|
8878
|
+
|
|
8879
|
+
* @param {Cli_Status_Folder_Linked_OnlyInputs} inputs
|
|
8880
|
+
* @param {{ locale?: "ko" }} options
|
|
8881
|
+
* @returns {LocalizedString}
|
|
8882
|
+
*/
|
|
8883
|
+
const cli_status_folder_linked_only = ((inputs = {}, options = {}) => {
|
|
8884
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8885
|
+
return ko_cli_status_folder_linked_only(inputs);
|
|
8886
|
+
});
|
|
8887
|
+
|
|
8537
8888
|
//#endregion
|
|
8538
8889
|
//#region src/paraglide/messages/cli_status_folder_other_site.js
|
|
8539
8890
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -9001,7 +9352,9 @@ const ko_cli_usage = (i) => {
|
|
|
9001
9352
|
npx sayren login --paste # 액세스 토큰을 입력받아 저장(셸 기록에 남지 않습니다)
|
|
9002
9353
|
npx sayren logout
|
|
9003
9354
|
npx sayren whoami
|
|
9004
|
-
npx sayren create <이름> [--
|
|
9355
|
+
npx sayren create <이름> [--template <템플릿>] # 시작 코드를 새 폴더에 만듭니다(파일만, 로그인 없음)
|
|
9356
|
+
npx sayren link [폴더] [--store <storeId>] # 폴더를 상점 사이트에 연결합니다(없으면 사이트를 만듭니다)
|
|
9357
|
+
npx sayren env pull # 연결된 상점 값을 .env에 씁니다
|
|
9005
9358
|
npx sayren init [폴더] [--template <id>] [--store <storeId>] [--yes]
|
|
9006
9359
|
npx sayren pull --project <storeId> [폴더] [--force [--yes]]
|
|
9007
9360
|
npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
|
|
@@ -9014,10 +9367,12 @@ const ko_cli_usage = (i) => {
|
|
|
9014
9367
|
login Sayren 계정으로 로그인합니다. 로그인 토큰은 사용자 설정 폴더(~/.config/sayren/credentials.json)에 둡니다
|
|
9015
9368
|
logout 로그인을 폐기하고 이 컴퓨터의 토큰을 지웁니다
|
|
9016
9369
|
whoami 쓰는 계정·토큰 종류·상점 범위
|
|
9017
|
-
create
|
|
9370
|
+
create 시작 코드(상점 이름·상품 목록 홈, SDK·문구·분석·에이전트 안내)를 <이름> 폴더에 만듭니다. 상점을 묻지 않고 호스팅 사이트도 만들지 않습니다
|
|
9371
|
+
link 상점을 고르고(--store → 토큰 범위 → 선택) 사이트가 없으면 만든 뒤 .sayren/project.json·.env·.mcp.json을 씁니다
|
|
9372
|
+
env env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL을 .env에 씁니다
|
|
9018
9373
|
init 템플릿을 골라 호스팅 사이트를 만들고 pull과 같은 폴더(기본: 상점 코드)로 받습니다. 사이트가 이미 있으면 만들지 않습니다
|
|
9019
|
-
pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/
|
|
9020
|
-
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 원격이
|
|
9374
|
+
pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/project.json을 씁니다
|
|
9375
|
+
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 연결이 없으면 link부터 하고 다음 버전으로 교체합니다. 원격이 마지막 받기·올리기 이후 바뀌었으면 덮어쓰지 않습니다
|
|
9021
9376
|
status 사이트·발행 버전·최근 빌드·편집 환경
|
|
9022
9377
|
verify 로컬 규칙·경로·크기 검사(서버 빌드와 같은 규칙)
|
|
9023
9378
|
skill sayren 스킬 목록(list)과 본문(<이름>)을 출력합니다. 스킬은 중앙에서 고치므로 늘 최신입니다
|
|
@@ -9030,8 +9385,9 @@ const ko_cli_usage = (i) => {
|
|
|
9030
9385
|
|
|
9031
9386
|
옵션:
|
|
9032
9387
|
--project <storeId> pull: 받을 상점(--store와 같습니다)
|
|
9033
|
-
--template
|
|
9034
|
-
|
|
9388
|
+
--template <템플릿> create: github:owner/repo[/경로][#ref](공개 GitHub 저장소) 또는 템플릿 id. 없으면 CLI에 든 시작 코드
|
|
9389
|
+
init: 호스팅 템플릿 id(비대화형은 필수)
|
|
9390
|
+
--store-code <코드> create: 이 상점 코드로 .env를 씁니다(없으면 link·env pull이 씁니다)
|
|
9035
9391
|
--dir <폴더> 작업 폴더(기본: 지금 폴더, pull·init은 상점 코드)
|
|
9036
9392
|
--force pull: 같은 사이트를 받은 폴더에 원격을 다시 받습니다(지울·덮을 파일을 보여 주고 묻습니다)
|
|
9037
9393
|
deploy: 원격 변경·열린 편집 환경을 무시하고 덮어씁니다(확인을 묻습니다)
|
|
@@ -9234,8 +9590,9 @@ function useProcessLocale(locale) {
|
|
|
9234
9590
|
/**
|
|
9235
9591
|
* 설정 (2026-09-29 사용자 결정: `.sayrenrc`를 없애고 받은 폴더에 원격 MCP `.mcp.json`을 둔다)
|
|
9236
9592
|
*
|
|
9237
|
-
* - `.sayren/state.json
|
|
9238
|
-
* 올리지 않는다. `baseRef`가 deploy의 `expectedBaseRef`다(원격이 pull 이후 바뀌었으면 `SOURCE_CHANGED`).
|
|
9593
|
+
* - `.sayren/project.json`(#315 C6, 옛 `.sayren/state.json`): link·pull·deploy가 쓰는 연결 정보(`storeId`·`siteId`·`baseRef`, 기본이 아닌 api 주소).
|
|
9594
|
+
* `.gitignore` 대상이고 올리지 않는다. `baseRef`가 deploy의 `expectedBaseRef`다(원격이 pull 이후 바뀌었으면 `SOURCE_CHANGED`). `link`가 막
|
|
9595
|
+
* 연결한 폴더는 `baseRef`가 null이다 — 첫 deploy가 조건 없이 다음 버전으로 교체하고(머리 없는 사이트는 첫 커밋), 그 뒤부터 올린 트리가 기준이다.
|
|
9239
9596
|
* - `.mcp.json`: Claude Code 형식의 `sayren` 원격 MCP 항목. 주소와 `X-Store-Id`만 있고 토큰은 없다(#71 — MCP 클라이언트가
|
|
9240
9597
|
* 연결할 때 스스로 로그인한다). 옛 폴더의 폐기된 토큰(`sy_pat_…`) 헤더는 쓸 때 지운다. `.gitignore` 대상이고 올리지 않는다.
|
|
9241
9598
|
* - `.env`: 스토어프론트 템플릿 개발 서버가 읽는 값(`SAYREN_API_URL`·`SAYREN_STORE_CODE`)만 둔다. 토큰을 두지 않는다.
|
|
@@ -9245,10 +9602,12 @@ function useProcessLocale(locale) {
|
|
|
9245
9602
|
* 옛 API 토큰(`sy_pat_…`)은 R1에서 폐기되어 api에 보내지 않고 거절한다(`cli_retired_token`).
|
|
9246
9603
|
* api 주소는 `--api-origin` → `SAYREN_API_ORIGIN` → `.env`의
|
|
9247
9604
|
* `SAYREN_API_URL` → 상태 파일 → 기본값 순서이고, 파일에서 온 기본이 아닌 주소는 확인한다(`checkAuthSources`).
|
|
9248
|
-
* 옛 폴더(`.sayrenrc`, `.env`의 `SAYREN_TOKEN`)는 한 번 정리한다(`migrateLegacy`).
|
|
9605
|
+
* 옛 폴더(`.sayrenrc`, `.sayren/state.json`, `.env`의 `SAYREN_TOKEN`)는 한 번 정리한다(`migrateLegacy`).
|
|
9249
9606
|
*/
|
|
9250
9607
|
const STATE_DIR = ".sayren";
|
|
9251
|
-
const STATE_FILE = `${STATE_DIR}/
|
|
9608
|
+
const STATE_FILE = `${STATE_DIR}/project.json`;
|
|
9609
|
+
/** 옛 연결 파일(0.2.0 ~ 0.14.x) — 읽고, 옮긴 뒤 지운다 */
|
|
9610
|
+
const LEGACY_STATE_FILE = `${STATE_DIR}/state.json`;
|
|
9252
9611
|
/** 옛 상태 파일(0.1.x) — 읽어서 옮기고 지운다 */
|
|
9253
9612
|
const LEGACY_RC_FILE = ".sayrenrc";
|
|
9254
9613
|
const ENV_FILE = ".env";
|
|
@@ -9263,7 +9622,7 @@ function parseState(text, name) {
|
|
|
9263
9622
|
}
|
|
9264
9623
|
if (!parsed || typeof parsed !== "object") throw new CliError(cli_state_invalid({ name }));
|
|
9265
9624
|
const raw = parsed;
|
|
9266
|
-
if (typeof raw.storeId !== "string" || typeof raw.siteId !== "string" || typeof raw.baseRef !== "string") throw new CliError(cli_state_missing_fields({ name }));
|
|
9625
|
+
if (typeof raw.storeId !== "string" || typeof raw.siteId !== "string" || typeof raw.baseRef !== "string" && raw.baseRef !== null) throw new CliError(cli_state_missing_fields({ name }));
|
|
9267
9626
|
return {
|
|
9268
9627
|
storeId: raw.storeId,
|
|
9269
9628
|
siteId: raw.siteId,
|
|
@@ -9271,10 +9630,13 @@ function parseState(text, name) {
|
|
|
9271
9630
|
...typeof raw.apiOrigin === "string" ? { apiOrigin: raw.apiOrigin } : {}
|
|
9272
9631
|
};
|
|
9273
9632
|
}
|
|
9633
|
+
/** 연결 정보 — `project.json`, 없으면 옛 `state.json`을 읽는다(옮기기는 `migrateLegacy`) */
|
|
9274
9634
|
function readState(dir) {
|
|
9275
|
-
const
|
|
9276
|
-
|
|
9277
|
-
|
|
9635
|
+
for (const name of [STATE_FILE, LEGACY_STATE_FILE]) {
|
|
9636
|
+
const path = join(dir, name);
|
|
9637
|
+
if (existsSync(path)) return parseState(readFileSync(path, "utf8"), name);
|
|
9638
|
+
}
|
|
9639
|
+
return null;
|
|
9278
9640
|
}
|
|
9279
9641
|
function writeState(dir, state) {
|
|
9280
9642
|
const out = {
|
|
@@ -9285,10 +9647,15 @@ function writeState(dir, state) {
|
|
|
9285
9647
|
if (state.apiOrigin && state.apiOrigin !== DEFAULT_API_ORIGIN) out.apiOrigin = state.apiOrigin;
|
|
9286
9648
|
mkdirSync(join(dir, STATE_DIR), { recursive: true });
|
|
9287
9649
|
writeFileSync(join(dir, STATE_FILE), `${JSON.stringify(out, null, 2)}\n`);
|
|
9650
|
+
rmSync(join(dir, LEGACY_STATE_FILE), { force: true });
|
|
9288
9651
|
}
|
|
9289
|
-
/** 받은 폴더인가 —
|
|
9652
|
+
/** 연결된(받은) 폴더인가 — 연결 파일 또는 옛 `state.json`·`.sayrenrc`가 있다 */
|
|
9290
9653
|
function isPulledDir(dir) {
|
|
9291
|
-
return
|
|
9654
|
+
return [
|
|
9655
|
+
STATE_FILE,
|
|
9656
|
+
LEGACY_STATE_FILE,
|
|
9657
|
+
LEGACY_RC_FILE
|
|
9658
|
+
].some((name) => existsSync(join(dir, name)));
|
|
9292
9659
|
}
|
|
9293
9660
|
function readMcpText(dir) {
|
|
9294
9661
|
const path = join(dir, MCP_FILE);
|
|
@@ -9327,12 +9694,18 @@ function writePrivate(path, text) {
|
|
|
9327
9694
|
}
|
|
9328
9695
|
}
|
|
9329
9696
|
/**
|
|
9330
|
-
*
|
|
9697
|
+
* 옛 CLI가 받은 폴더를 한 번 정리한다. `.sayren/state.json`(0.2.0 ~ 0.14.x)·`.sayrenrc`(0.1.x) → `.sayren/project.json`(옛 파일은 지운다), `.env`의 폐기된 `SAYREN_TOKEN`(`sy_pat_…`)은
|
|
9331
9698
|
* 지운다(R1 뒤 쓸 수 없다). 다른 값이 든 `SAYREN_TOKEN` 줄은 두고(CLI는 읽지 않는다) 알린다. 정리했으면 한 줄로 알린다.
|
|
9332
9699
|
*/
|
|
9333
9700
|
function migrateLegacy(dir, notify) {
|
|
9334
9701
|
if (!isPulledDir(dir)) return;
|
|
9335
9702
|
const done = [];
|
|
9703
|
+
const legacyState = join(dir, LEGACY_STATE_FILE);
|
|
9704
|
+
if (existsSync(legacyState)) {
|
|
9705
|
+
if (existsSync(join(dir, STATE_FILE))) rmSync(legacyState, { force: true });
|
|
9706
|
+
else writeState(dir, parseState(readFileSync(legacyState, "utf8"), LEGACY_STATE_FILE));
|
|
9707
|
+
done.push(`${LEGACY_STATE_FILE} → ${STATE_FILE}`);
|
|
9708
|
+
}
|
|
9336
9709
|
const rcPath = join(dir, LEGACY_RC_FILE);
|
|
9337
9710
|
if (existsSync(rcPath)) {
|
|
9338
9711
|
const tracked = isGitTracked(dir, LEGACY_RC_FILE);
|
|
@@ -9953,7 +10326,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9953
10326
|
|
|
9954
10327
|
//#endregion
|
|
9955
10328
|
//#region package.json
|
|
9956
|
-
var version = "0.
|
|
10329
|
+
var version = "0.15.1";
|
|
9957
10330
|
|
|
9958
10331
|
//#endregion
|
|
9959
10332
|
//#region src/client.ts
|
|
@@ -11626,42 +11999,49 @@ const FORBIDDEN_SEGMENTS = new Set([
|
|
|
11626
11999
|
".nitro",
|
|
11627
12000
|
".vinxi"
|
|
11628
12001
|
]);
|
|
11629
|
-
/**
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
12002
|
+
/**
|
|
12003
|
+
* 작업 트리에 두지 않는 이름(어디에 있든) — 형식은 맞지만 추적하지 않는다(목록·스냅샷·업로드·쓰기에서 빠진다, #298 P0 거절 목록).
|
|
12004
|
+
* - `.dev.vars`·`.dev.vars.*`(이름 규칙은 `isDevVars`): wrangler 로컬 개발 비밀 값 파일. `.env*`와 같이 업로드·스냅샷·Git push에 들어가지 않게
|
|
12005
|
+
* - `.sayren`·`.sayrenrc`: CLI 연결·상태 파일(`.sayren/project.json`, 옛 `.sayrenrc`는 토큰이 든다). 받기(pull)가 로컬 연결을 덮지 않게
|
|
12006
|
+
* - `.npmrc`·`.yarnrc`·`.yarnrc.yml`·`bunfig.toml`: 레지스트리 인증 토큰이 들어가는 설치 설정. 레지스트리·스크립트 설정이 의존성 판정을 비켜 가지 않게
|
|
12007
|
+
* - `.pnpmfile.cjs`·`.pnpmfile.mjs`: 설치 중 실행되는 훅(`--ignore-scripts`와 무관하게 돈다). 판정한 설치 트리를 바꾸지 않게
|
|
12008
|
+
* - `coverage`·`.turbo`·`.cache`: 테스트·도구 산출물. `.DS_Store`·`Thumbs.db`: OS 부산물
|
|
12009
|
+
*/
|
|
12010
|
+
const UNTRACKED_SEGMENTS = new Set([
|
|
12011
|
+
".sayren",
|
|
12012
|
+
".sayrenrc",
|
|
12013
|
+
".npmrc",
|
|
12014
|
+
".yarnrc",
|
|
12015
|
+
".yarnrc.yml",
|
|
12016
|
+
"bunfig.toml",
|
|
12017
|
+
".pnpmfile.cjs",
|
|
12018
|
+
".pnpmfile.mjs",
|
|
12019
|
+
"coverage",
|
|
12020
|
+
".turbo",
|
|
12021
|
+
".cache",
|
|
12022
|
+
".DS_Store",
|
|
12023
|
+
"Thumbs.db"
|
|
11637
12024
|
]);
|
|
12025
|
+
/** wrangler 로컬 비밀 값 파일(`.dev.vars`, 환경별 `.dev.vars.<env>`) — 어디에 있든 추적하지 않는다 */
|
|
12026
|
+
const isDevVars = (segment) => segment === ".dev.vars" || segment.startsWith(".dev.vars.");
|
|
12027
|
+
/** 루트에 두지 않는 이름 — `pnpm-workspace.yaml`은 사이트를 워크스페이스로 바꾸고 설치 설정(overrides 등)을 싣는다 */
|
|
12028
|
+
const UNTRACKED_ROOT_FILES = new Set(["pnpm-workspace.yaml"]);
|
|
11638
12029
|
/**
|
|
11639
|
-
* 읽기만 되는 루트 파일 —
|
|
11640
|
-
* `
|
|
12030
|
+
* 읽기만 되는 루트 파일 — `CLAUDE.md`·`.mcp.json`은 시작 코드가 싣는 에이전트 안내·MCP 연결 설정이다. 편집 도구는 바꾸지 않는다.
|
|
12031
|
+
* `package.json`·잠금 파일은 쓸 수 있다(#298 P0) — 바뀌면 api가 의존성을 다시 검사한다
|
|
11641
12032
|
*/
|
|
11642
|
-
const READONLY_ROOT_FILES = new Set([
|
|
11643
|
-
|
|
11644
|
-
"pnpm-lock.yaml",
|
|
11645
|
-
"CLAUDE.md",
|
|
11646
|
-
".mcp.json"
|
|
11647
|
-
]);
|
|
11648
|
-
/** 읽기만 되는 폴더 — 템플릿이 싣는 Claude 스킬(`.claude/skills/…`, 2026-10-14). 셀러 로컬 Claude Code와 AI 상점 도우미가 같은 기준을 읽는다 */
|
|
12033
|
+
const READONLY_ROOT_FILES = new Set(["CLAUDE.md", ".mcp.json"]);
|
|
12034
|
+
/** 읽기만 되는 폴더 — 시작 코드가 싣는 Claude 스킬(`.claude/skills/…`, 2026-10-14). 셀러 로컬 Claude Code와 AI가 같은 기준을 읽는다 */
|
|
11649
12035
|
const READONLY_DIRS = new Set([".claude"]);
|
|
11650
12036
|
/**
|
|
11651
|
-
*
|
|
11652
|
-
*
|
|
12037
|
+
* 스냅샷 묶기에서 빼는 이름(`container-tree.ts`의 `--exclude`) — 금지 조각과 추적하지 않는 이름. 최종 거름은 `isTracked`다
|
|
12038
|
+
* (여기는 큰 설치물·산출물을 tar에 넣지 않으려는 것이다)
|
|
11653
12039
|
*/
|
|
11654
|
-
const
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
"
|
|
11658
|
-
|
|
11659
|
-
/** 작업 트리 루트에 둘 수 있는 이름(폴더·파일) — 컨테이너 스냅샷이 이 이름만 묶는다(`container-tree.ts`) */
|
|
11660
|
-
const TRACKED_ROOT_NAMES = [
|
|
11661
|
-
...WRITABLE_DIRS,
|
|
11662
|
-
...READONLY_DIRS,
|
|
11663
|
-
...WRITABLE_ROOT_FILES,
|
|
11664
|
-
...READONLY_ROOT_FILES
|
|
12040
|
+
const SNAPSHOT_EXCLUDED_NAMES = [
|
|
12041
|
+
...FORBIDDEN_SEGMENTS,
|
|
12042
|
+
...UNTRACKED_SEGMENTS,
|
|
12043
|
+
".dev.vars",
|
|
12044
|
+
".dev.vars.*"
|
|
11665
12045
|
];
|
|
11666
12046
|
/** 형식·금지 세그먼트만 본다(위치 규칙은 보지 않는다). 통과하면 null, 아니면 이유 */
|
|
11667
12047
|
function formatProblem(path) {
|
|
@@ -11678,21 +12058,24 @@ function formatProblem(path) {
|
|
|
11678
12058
|
}
|
|
11679
12059
|
return null;
|
|
11680
12060
|
}
|
|
11681
|
-
/**
|
|
12061
|
+
/** 추적하지 않는 이름이면 이유, 아니면 null(형식은 이미 통과한 경로) */
|
|
12062
|
+
function untrackedReason(segments) {
|
|
12063
|
+
if (segments.some((s) => UNTRACKED_SEGMENTS.has(s) || isDevVars(s))) return "untracked";
|
|
12064
|
+
if (segments.length === 1 && UNTRACKED_ROOT_FILES.has(segments[0])) return "untracked";
|
|
12065
|
+
return null;
|
|
12066
|
+
}
|
|
12067
|
+
/**
|
|
12068
|
+
* 위치 규칙: 어떤 모드로 이 경로에 닿을 수 있는가. 거절 목록 방식이다(#298 P0) — 형식(금지 조각·`.env*`·문자·길이)과 추적하지 않는 이름만
|
|
12069
|
+
* 막고 나머지는 어디든 쓸 수 있다. 예외는 읽기만 되는 에이전트 안내(`CLAUDE.md`·`.mcp.json`·`.claude/**`)다
|
|
12070
|
+
*/
|
|
11682
12071
|
function accessOf(path) {
|
|
11683
12072
|
if (formatProblem(path) !== null) return null;
|
|
11684
12073
|
const segments = path.split("/");
|
|
12074
|
+
if (untrackedReason(segments) !== null) return null;
|
|
11685
12075
|
const [first] = segments;
|
|
11686
|
-
if (
|
|
11687
|
-
if (segments.length
|
|
11688
|
-
|
|
11689
|
-
if (READONLY_ROOT_FILES.has(first)) return "read";
|
|
11690
|
-
return null;
|
|
11691
|
-
}
|
|
11692
|
-
const readonlyDir = READONLY_DIRS.has(first);
|
|
11693
|
-
if (!readonlyDir && !WRITABLE_DIRS.has(first)) return null;
|
|
11694
|
-
if (segments.slice(1).some((s) => s.startsWith("."))) return null;
|
|
11695
|
-
return readonlyDir ? "read" : "write";
|
|
12076
|
+
if (segments.length === 1 && READONLY_ROOT_FILES.has(first)) return "read";
|
|
12077
|
+
if (segments.length > 1 && READONLY_DIRS.has(first)) return "read";
|
|
12078
|
+
return "write";
|
|
11696
12079
|
}
|
|
11697
12080
|
|
|
11698
12081
|
//#endregion
|
|
@@ -11806,18 +12189,19 @@ async function gunzip(bytes, maxBytes) {
|
|
|
11806
12189
|
//#endregion
|
|
11807
12190
|
//#region src/tree.ts
|
|
11808
12191
|
/**
|
|
11809
|
-
* 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다.
|
|
11810
|
-
*
|
|
12192
|
+
* 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다. 거절 목록 방식이다(#298 P0): 폴더 구조와
|
|
12193
|
+
* 무관하게 올리고, `.env*`·`.mcp.json`·`.sayren/`·`.npmrc`·`node_modules`·`dist` 등 설치물·산출물·비밀·로컬 상태만 올리지 않는다.
|
|
11811
12194
|
* 비밀·로컬 상태 파일(`isNeverUpload`)은 서버 경로 규칙과 별개로 CLI가 먼저 막는다 — 서버 규칙이 넓어져도 토큰을 올리지 않게.
|
|
11812
12195
|
*/
|
|
11813
12196
|
/**
|
|
11814
|
-
* 어떤 경우에도 올리지 않는 파일 — `.env*`(개발 서버 값), `.mcp.json`(토큰), `.sayren/`(CLI 상태),
|
|
12197
|
+
* 어떤 경우에도 올리지 않는 파일 — `.env*`(개발 서버 값), `.dev.vars*`(wrangler 로컬 비밀 값), `.mcp.json`(토큰), `.sayren/`(CLI 상태),
|
|
12198
|
+
* 옛 `.sayrenrc`, `.npmrc`(레지스트리 토큰).
|
|
11815
12199
|
* 폴더 깊이와 무관하게 이름으로 본다.
|
|
11816
12200
|
*/
|
|
11817
12201
|
function isNeverUpload(path) {
|
|
11818
12202
|
const segments = path.split("/");
|
|
11819
12203
|
const name = segments[segments.length - 1] ?? "";
|
|
11820
|
-
return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren") || name === ".mcp.json" || name === ".sayrenrc";
|
|
12204
|
+
return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren" || segment === ".dev.vars" || segment.startsWith(".dev.vars.")) || name === ".mcp.json" || name === ".sayrenrc" || name === ".npmrc";
|
|
11821
12205
|
}
|
|
11822
12206
|
/** 걸어 들어가지 않는 폴더 — 설치물·산출물·캐시·VCS(경로 규칙이 어차피 막는다. 크기 때문에 읽지도 않는다) */
|
|
11823
12207
|
const SKIP_DIRS$1 = new Set([
|
|
@@ -11977,7 +12361,7 @@ function runLocalChecks(tree) {
|
|
|
11977
12361
|
if (packageJson && lock) {
|
|
11978
12362
|
const drift = lockDrift(packageJson, lock);
|
|
11979
12363
|
if (drift.length) warnings.push(cli_checks_lock_drift({ names: drift.slice(0, 5).join(", ") }));
|
|
11980
|
-
}
|
|
12364
|
+
}
|
|
11981
12365
|
return {
|
|
11982
12366
|
report: report$1,
|
|
11983
12367
|
blockingRules,
|
|
@@ -12380,6 +12764,107 @@ async function pickTemplate(templates, wanted, io) {
|
|
|
12380
12764
|
if (index < 0) throw new CliError(cli_init_template_required({ list: labels.map((label) => ` ${label}`).join("\n") }));
|
|
12381
12765
|
return templates[index];
|
|
12382
12766
|
}
|
|
12767
|
+
/**
|
|
12768
|
+
* `sayren link [폴더]`(#315 C6) — 폴더를 상점의 호스팅 사이트에 연결한다. 상점은 `--store` → 토큰 범위 → 대화형 선택이고, 사이트가 없으면
|
|
12769
|
+
* 시작 코드 없이 만든다(`initialSource: "NONE"` — 오픈 준비 중·버전 없음, 첫 deploy가 첫 커밋). 연결 정보는 `.sayren/project.json`, 상점 값은
|
|
12770
|
+
* `env pull`과 같이 `.env`에 쓴다. 토큰은 어느 파일에도 쓰지 않는다.
|
|
12771
|
+
*/
|
|
12772
|
+
async function link(flags, io) {
|
|
12773
|
+
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12774
|
+
if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
|
|
12775
|
+
migrateLegacy(dir, io.out);
|
|
12776
|
+
await linkDir(flags, dir, io);
|
|
12777
|
+
io.out(cli_link_next_steps());
|
|
12778
|
+
return 0;
|
|
12779
|
+
}
|
|
12780
|
+
/** link 본체 — `deploy`도 연결이 없으면 이것부터 부른다 */
|
|
12781
|
+
async function linkDir(flags, dir, io) {
|
|
12782
|
+
let previous = null;
|
|
12783
|
+
try {
|
|
12784
|
+
previous = readState(dir);
|
|
12785
|
+
} catch {
|
|
12786
|
+
previous = null;
|
|
12787
|
+
}
|
|
12788
|
+
const { auth, client } = await apiFor(flags, dir, previous, io);
|
|
12789
|
+
const storeId = auth.storeId;
|
|
12790
|
+
const hosting = await client.storefront.get();
|
|
12791
|
+
if (!hosting.editingEnabled) throw new CliError(cli_link_editing_disabled());
|
|
12792
|
+
let site = hosting.site;
|
|
12793
|
+
if (site) {
|
|
12794
|
+
const git$1 = await client.storefront.getGit().catch(() => null);
|
|
12795
|
+
if (git$1?.owner === "SELLER" && git$1.repository) throw new CliError(cli_deploy_seller_git({
|
|
12796
|
+
repository: git$1.repository,
|
|
12797
|
+
branch: git$1.productionBranch ?? "main"
|
|
12798
|
+
}));
|
|
12799
|
+
} else try {
|
|
12800
|
+
site = await client.storefront.create({ initialSource: "NONE" });
|
|
12801
|
+
io.out(cli_link_site_created({ url: site.url }));
|
|
12802
|
+
} catch (error) {
|
|
12803
|
+
const code = codeOf(error);
|
|
12804
|
+
if (code === "STOREFRONT_SLUG_TAKEN") throw new CliError(cli_link_slug_taken());
|
|
12805
|
+
if (code !== "STOREFRONT_SITE_EXISTS") throw error;
|
|
12806
|
+
site = (await client.storefront.get()).site;
|
|
12807
|
+
if (!site) throw error;
|
|
12808
|
+
}
|
|
12809
|
+
const same = previous?.storeId === storeId && previous.siteId === site.siteId;
|
|
12810
|
+
const state = {
|
|
12811
|
+
storeId,
|
|
12812
|
+
siteId: site.siteId,
|
|
12813
|
+
baseRef: same ? previous?.baseRef ?? null : null,
|
|
12814
|
+
...auth.apiOrigin !== DEFAULT_API_ORIGIN ? { apiOrigin: auth.apiOrigin } : {}
|
|
12815
|
+
};
|
|
12816
|
+
writeState(dir, state);
|
|
12817
|
+
const storeCode = await storeCodeOf(client, storeId);
|
|
12818
|
+
writeStoreEnv(dir, auth.apiOrigin, storeCode);
|
|
12819
|
+
const addedIgnore = ensureGitignore(dir);
|
|
12820
|
+
if (writeMcpJson(dir, { storeId }) === "retired-token-removed") io.out(cli_notice({ message: retiredMcpTokenNotice() }));
|
|
12821
|
+
io.out(cli_link_done({
|
|
12822
|
+
storeId,
|
|
12823
|
+
url: site.url,
|
|
12824
|
+
file: STATE_FILE,
|
|
12825
|
+
envFile: ENV_FILE,
|
|
12826
|
+
storeCode
|
|
12827
|
+
}));
|
|
12828
|
+
if (addedIgnore.length) io.out(cli_link_gitignore({ lines: addedIgnore.join(" ") }));
|
|
12829
|
+
return {
|
|
12830
|
+
client,
|
|
12831
|
+
state
|
|
12832
|
+
};
|
|
12833
|
+
}
|
|
12834
|
+
/** 상점 코드 — 계정의 상점 목록(`stores:read`), 안 되면 작업 트리 조회(`storefront:r`) */
|
|
12835
|
+
async function storeCodeOf(client, storeId) {
|
|
12836
|
+
const listed = await client.auth.listMyStores().then((stores) => stores.find((store) => store.storeId === storeId)?.storeCode).catch(() => void 0);
|
|
12837
|
+
if (listed) return listed;
|
|
12838
|
+
const fromSource = await client.storefront.getSource().then((source) => source.storeCode).catch(() => void 0);
|
|
12839
|
+
if (fromSource) return fromSource;
|
|
12840
|
+
throw new CliError(cli_env_store_code_unknown());
|
|
12841
|
+
}
|
|
12842
|
+
/** `.env`의 상점 연결 값 — 다른 줄은 둔다. 토큰 줄(`SAYREN_TOKEN`)은 지운다 */
|
|
12843
|
+
function writeStoreEnv(dir, apiOrigin, storeCode) {
|
|
12844
|
+
updateEnvFile(dir, {
|
|
12845
|
+
SAYREN_API_URL: `${apiOrigin}/storefront/v1`,
|
|
12846
|
+
SAYREN_STORE_CODE: storeCode
|
|
12847
|
+
}, ["SAYREN_TOKEN"]);
|
|
12848
|
+
}
|
|
12849
|
+
/** `sayren env pull` — 연결된 상점의 `SAYREN_STORE_CODE`·`SAYREN_API_URL`을 `.env`에 쓴다 */
|
|
12850
|
+
async function envPull(flags, io) {
|
|
12851
|
+
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12852
|
+
migrateLegacy(dir, io.out);
|
|
12853
|
+
const state = readState(dir);
|
|
12854
|
+
if (!state) throw new CliError(cli_env_not_linked({ file: STATE_FILE }));
|
|
12855
|
+
const { auth, client } = await apiFor({
|
|
12856
|
+
...flags,
|
|
12857
|
+
store: state.storeId
|
|
12858
|
+
}, dir, state, io);
|
|
12859
|
+
const storeCode = await storeCodeOf(client, state.storeId);
|
|
12860
|
+
writeStoreEnv(dir, auth.apiOrigin, storeCode);
|
|
12861
|
+
ensureGitignore(dir);
|
|
12862
|
+
io.out(cli_env_pulled({
|
|
12863
|
+
file: ENV_FILE,
|
|
12864
|
+
storeCode
|
|
12865
|
+
}));
|
|
12866
|
+
return 0;
|
|
12867
|
+
}
|
|
12383
12868
|
async function verify(flags, io) {
|
|
12384
12869
|
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12385
12870
|
const tree = collectTree(dir);
|
|
@@ -12414,38 +12899,30 @@ function printChecks(tree, checks, io) {
|
|
|
12414
12899
|
}) : cli_skipped_outside({ paths })}`);
|
|
12415
12900
|
}
|
|
12416
12901
|
}
|
|
12902
|
+
/**
|
|
12903
|
+
* `deploy` — 연결 정보(`.sayren/project.json`)가 없으면 `link`부터 한다(#315 C6, Vercel 방식). 막 연결한 폴더는 기준(`baseRef`)이 없어
|
|
12904
|
+
* 확인 없이 다음 버전으로 교체한다(사이트에 소스가 없으면 첫 커밋). 그 뒤 deploy는 올린 트리를 기준으로 조건부 교체한다.
|
|
12905
|
+
*/
|
|
12417
12906
|
async function deploy(flags, io) {
|
|
12418
12907
|
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12419
12908
|
migrateLegacy(dir, io.out);
|
|
12420
|
-
const
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
try {
|
|
12424
|
-
storeId = readMcpStoreId(dir);
|
|
12425
|
-
} catch {
|
|
12426
|
-
storeId = null;
|
|
12427
|
-
}
|
|
12428
|
-
const git$1 = await apiFor(flags, dir, null, {
|
|
12429
|
-
...io,
|
|
12430
|
-
select: null,
|
|
12431
|
-
confirm: null
|
|
12432
|
-
}).then(({ client: client$1 }) => client$1.storefront.getGit()).catch(() => null);
|
|
12433
|
-
if (git$1?.owner === "SELLER" && git$1.repository) throw new CliError(cli_deploy_seller_git({
|
|
12434
|
-
repository: git$1.repository,
|
|
12435
|
-
branch: git$1.productionBranch ?? "main"
|
|
12436
|
-
}));
|
|
12437
|
-
throw new CliError(cli_deploy_no_state({
|
|
12438
|
-
stateFile: STATE_FILE,
|
|
12439
|
-
storeId: storeId ?? "<storeId>",
|
|
12440
|
-
dir: relativeOf(io, dir)
|
|
12441
|
-
}));
|
|
12442
|
-
}
|
|
12443
|
-
const { client } = await apiFor(flags, dir, rc, io);
|
|
12444
|
-
const tree = collectTree(dir);
|
|
12909
|
+
const prior = readState(dir);
|
|
12910
|
+
const linked = prior ? await apiFor(flags, dir, prior, io) : null;
|
|
12911
|
+
let tree = collectTree(dir);
|
|
12445
12912
|
const checks = runLocalChecks(tree);
|
|
12446
12913
|
printChecks(tree, checks, io);
|
|
12447
12914
|
if (checks.blockingRules.length > 0 || checks.problems.length > 0) throw new CliError(cli_deploy_checks_failed());
|
|
12448
12915
|
await localBuild(dir, flags.skipBuild === true, io);
|
|
12916
|
+
let client;
|
|
12917
|
+
let rc;
|
|
12918
|
+
if (linked && prior) {
|
|
12919
|
+
client = linked.client;
|
|
12920
|
+
rc = prior;
|
|
12921
|
+
} else {
|
|
12922
|
+
io.out(cli_deploy_linking({ dir: relativeOf(io, dir) }));
|
|
12923
|
+
({client, state: rc} = await linkDir(flags, dir, io));
|
|
12924
|
+
tree = collectTree(dir);
|
|
12925
|
+
}
|
|
12449
12926
|
const site = (await client.storefront.get()).site;
|
|
12450
12927
|
if (!site || site.siteId !== rc.siteId) throw new CliError(cli_deploy_site_mismatch({ siteId: rc.siteId }));
|
|
12451
12928
|
if (flags.force && !flags.yes) {
|
|
@@ -12499,8 +12976,21 @@ async function deploy(flags, io) {
|
|
|
12499
12976
|
io.out(cli_deploy_not_published({ version: created.version.number }));
|
|
12500
12977
|
return 0;
|
|
12501
12978
|
}
|
|
12979
|
+
let current = site.publishedVersion;
|
|
12980
|
+
if (!current) {
|
|
12981
|
+
const after = (await client.storefront.get()).site;
|
|
12982
|
+
if (after?.publishedVersion?.versionId === created.version.versionId) {
|
|
12983
|
+
io.out(cli_deploy_published({
|
|
12984
|
+
version: created.version.number,
|
|
12985
|
+
url: after.url
|
|
12986
|
+
}));
|
|
12987
|
+
io.out(cli_deploy_publish_delay());
|
|
12988
|
+
return 0;
|
|
12989
|
+
}
|
|
12990
|
+
current = after?.publishedVersion ?? null;
|
|
12991
|
+
}
|
|
12502
12992
|
try {
|
|
12503
|
-
const published = await client.storefront.publish(created.version.versionId, { expectedPublishedVersionId:
|
|
12993
|
+
const published = await client.storefront.publish(created.version.versionId, { expectedPublishedVersionId: current?.versionId ?? null });
|
|
12504
12994
|
io.out(cli_deploy_published({
|
|
12505
12995
|
version: created.version.number,
|
|
12506
12996
|
url: published.url
|
|
@@ -12567,7 +13057,7 @@ async function replaceSource(client, tree, rc, force, io) {
|
|
|
12567
13057
|
const packageJson = textOf("package.json");
|
|
12568
13058
|
const lockfile = textOf("pnpm-lock.yaml");
|
|
12569
13059
|
const body = {
|
|
12570
|
-
...force ? { endEditSession: true } : { expectedBaseRef: rc.baseRef },
|
|
13060
|
+
...force ? { endEditSession: true } : rc.baseRef ? { expectedBaseRef: rc.baseRef } : {},
|
|
12571
13061
|
files,
|
|
12572
13062
|
...packageJson === void 0 ? {} : { packageJson },
|
|
12573
13063
|
...lockfile === void 0 ? {} : { lockfile }
|
|
@@ -12739,6 +13229,7 @@ async function status(flags, io) {
|
|
|
12739
13229
|
io.out(` ${editing.session ? cli_status_editing_open({ status: editing.session.status }) : cli_status_editing_closed()}`);
|
|
12740
13230
|
}
|
|
12741
13231
|
if (rc) if (rc.siteId !== site.siteId) io.out(` ${cli_status_folder_other_site({ siteId: rc.siteId })}`);
|
|
13232
|
+
else if (!rc.baseRef) io.out(` ${cli_status_folder_linked_only()}`);
|
|
12742
13233
|
else io.out(` ${cli_status_folder_base({ ref: rc.baseRef.slice(rc.baseRef.lastIndexOf("/") + 1, -4).slice(0, 12) })}`);
|
|
12743
13234
|
return 0;
|
|
12744
13235
|
}
|
|
@@ -12871,6 +13362,57 @@ function relativeOf(io, path) {
|
|
|
12871
13362
|
return relative(io.cwd, path) || ".";
|
|
12872
13363
|
}
|
|
12873
13364
|
|
|
13365
|
+
//#endregion
|
|
13366
|
+
//#region ../mcp/src/starter-transform.ts
|
|
13367
|
+
var StarterTransformError = class extends Error {
|
|
13368
|
+
code;
|
|
13369
|
+
constructor(code, message) {
|
|
13370
|
+
super(message);
|
|
13371
|
+
this.name = "StarterTransformError";
|
|
13372
|
+
this.code = code;
|
|
13373
|
+
}
|
|
13374
|
+
};
|
|
13375
|
+
/** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결 정보 */
|
|
13376
|
+
function isStarterLocalOnly(path) {
|
|
13377
|
+
if (path === ".sayren" || path.startsWith(".sayren/")) return true;
|
|
13378
|
+
if (path.includes("/")) return false;
|
|
13379
|
+
return (path === ".env" || path.startsWith(".env.")) && path !== ".env.example";
|
|
13380
|
+
}
|
|
13381
|
+
function transformStarterFiles(files, options) {
|
|
13382
|
+
const out = /* @__PURE__ */ new Map();
|
|
13383
|
+
for (const [path, bytes] of files) {
|
|
13384
|
+
if (isStarterLocalOnly(path)) continue;
|
|
13385
|
+
const slash = path.lastIndexOf("/");
|
|
13386
|
+
const base = path.slice(slash + 1);
|
|
13387
|
+
out.set(base === "_gitignore" ? `${path.slice(0, slash + 1)}.gitignore` : path, bytes);
|
|
13388
|
+
}
|
|
13389
|
+
const raw = out.get("package.json");
|
|
13390
|
+
if (!raw) throw new StarterTransformError("STARTER_PACKAGE_JSON_MISSING", "package.json이 없습니다");
|
|
13391
|
+
const next = withPackageName(raw, options.name, { private: true });
|
|
13392
|
+
if (!next) throw new StarterTransformError("STARTER_PACKAGE_JSON_INVALID", "package.json을 JSON으로 읽을 수 없습니다");
|
|
13393
|
+
out.set("package.json", next);
|
|
13394
|
+
return out;
|
|
13395
|
+
}
|
|
13396
|
+
/**
|
|
13397
|
+
* `package.json`의 `name`을 바꾼다(`fields`가 있으면 그 필드도). 나머지 필드·순서는 그대로다. JSON 객체가 아니면 null이다.
|
|
13398
|
+
* 편집 Sandbox의 시작 코드 사이트(이미 정규화된 R2 원본)는 이것만 부른다 — 서버가 바꾸는 것은 `name` 하나다.
|
|
13399
|
+
*/
|
|
13400
|
+
function withPackageName(bytes, name, fields = {}) {
|
|
13401
|
+
let pkg;
|
|
13402
|
+
try {
|
|
13403
|
+
pkg = JSON.parse(new TextDecoder().decode(bytes));
|
|
13404
|
+
} catch {
|
|
13405
|
+
return null;
|
|
13406
|
+
}
|
|
13407
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return null;
|
|
13408
|
+
const next = {
|
|
13409
|
+
...pkg,
|
|
13410
|
+
name,
|
|
13411
|
+
...fields
|
|
13412
|
+
};
|
|
13413
|
+
return new TextEncoder().encode(`${JSON.stringify(next, null, 2)}\n`);
|
|
13414
|
+
}
|
|
13415
|
+
|
|
12874
13416
|
//#endregion
|
|
12875
13417
|
//#region ../mcp/src/template-package.ts
|
|
12876
13418
|
/** package.json의 기준 템플릿 표식 필드 — 원천은 이 함수다(api `template-marker.ts`가 같은 모양을 읽는다) */
|
|
@@ -12951,11 +13493,13 @@ function readWorkspaceVersions(packagesDir) {
|
|
|
12951
13493
|
//#endregion
|
|
12952
13494
|
//#region src/create-command.ts
|
|
12953
13495
|
/**
|
|
12954
|
-
* `npx sayren create
|
|
12955
|
-
*
|
|
12956
|
-
*
|
|
13496
|
+
* `npx sayren create <이름> [--template <템플릿>]`(#271, #315 C7) — 시작 코드를 새 폴더에 쓴다. 파일만 만들고 상점을 묻지 않는다.
|
|
13497
|
+
* 상점 연결은 `sayren link`(사이트·`.sayren/project.json`)와 `sayren env pull`(`.env`)이 한다. `--store-code`를 주면 그 값으로 `.env`만 쓴다.
|
|
13498
|
+
*
|
|
13499
|
+
* 템플릿: 없으면 CLI에 묶은 시작 코드(네트워크 없음), `github:owner/repo[/경로][#ref]`는 codeload tarball, 그 밖의 `<id>`는 sayren 레지스트리
|
|
13500
|
+
* 자리다(마켓 전까지 늘 「템플릿을 찾을 수 없습니다」). 받은 소스는 변환 함수 하나(`starter-transform.ts`, 서버와 공유)를 지난다.
|
|
12957
13501
|
*/
|
|
12958
|
-
/**
|
|
13502
|
+
/** 묶은 시작 코드 위치 — 게시본은 빌드가 묶은 `dist/boilerplate`, 저장소에서 바로 돌리면 원천 `packages/cli/boilerplate` */
|
|
12959
13503
|
function boilerplateRoot() {
|
|
12960
13504
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
12961
13505
|
const candidates = [join(here, "boilerplate"), join(here, "..", "boilerplate")];
|
|
@@ -12963,18 +13507,7 @@ function boilerplateRoot() {
|
|
|
12963
13507
|
if (!found) throw new Error(`boilerplate not found: ${candidates.join(", ")}`);
|
|
12964
13508
|
return found;
|
|
12965
13509
|
}
|
|
12966
|
-
/**
|
|
12967
|
-
const BOILERPLATE_META_FILE = ".boilerplate-meta.json";
|
|
12968
|
-
function metaOf(root) {
|
|
12969
|
-
const bundled = join(root, BOILERPLATE_META_FILE);
|
|
12970
|
-
if (existsSync(bundled)) return JSON.parse(readFileSync(bundled, "utf8"));
|
|
12971
|
-
const repo = join(root, "..", "..", "..");
|
|
12972
|
-
return {
|
|
12973
|
-
catalog: parseCatalog(readFileSync(join(repo, "pnpm-workspace.yaml"), "utf8")),
|
|
12974
|
-
versions: readWorkspaceVersions(join(repo, "packages"))
|
|
12975
|
-
};
|
|
12976
|
-
}
|
|
12977
|
-
/** 싣지 않는 것 — 설치물·산출물·로컬 env. 점 파일은 에이전트 안내(`.claude/`·`.mcp.json`)와 `.gitignore`만 */
|
|
13510
|
+
/** 싣지 않는 것 — 설치물·산출물. 점 파일은 에이전트 안내(`.claude/`·`.mcp.json`)와 `.gitignore`만(scripts/starter-bundle.mjs와 같다) */
|
|
12978
13511
|
const SKIP_DIRS = new Set([
|
|
12979
13512
|
"node_modules",
|
|
12980
13513
|
"dist",
|
|
@@ -12988,8 +13521,11 @@ const DOT_KEPT = new Set([
|
|
|
12988
13521
|
".mcp.json",
|
|
12989
13522
|
".gitignore"
|
|
12990
13523
|
]);
|
|
12991
|
-
/**
|
|
12992
|
-
|
|
13524
|
+
/**
|
|
13525
|
+
* 묶은 시작 코드 — 상대 경로 → 내용. 게시본은 빌드가 이미 npm 범위로 풀었다. 저장소 원천(워크스페이스 구성원)에서 바로 돌리면
|
|
13526
|
+
* `catalog:`·`workspace:`를 여기서 푼다(게시본과 같은 결과).
|
|
13527
|
+
*/
|
|
13528
|
+
function bundledStarterFiles(root) {
|
|
12993
13529
|
const files = /* @__PURE__ */ new Map();
|
|
12994
13530
|
const walk = (dir) => {
|
|
12995
13531
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
@@ -12997,16 +13533,86 @@ function boilerplateFiles(root, name) {
|
|
|
12997
13533
|
if (entry.name.startsWith(".") && !DOT_KEPT.has(entry.name)) continue;
|
|
12998
13534
|
const full = join(dir, entry.name);
|
|
12999
13535
|
if (entry.isDirectory()) walk(full);
|
|
13000
|
-
else if (entry.isFile())
|
|
13001
|
-
const path = relative(root, full).split(sep).join("/");
|
|
13002
|
-
files.set(path === "_gitignore" ? ".gitignore" : path, readFileSync(full));
|
|
13003
|
-
}
|
|
13536
|
+
else if (entry.isFile()) files.set(relative(root, full).split(sep).join("/"), readFileSync(full));
|
|
13004
13537
|
}
|
|
13005
13538
|
};
|
|
13006
13539
|
walk(root);
|
|
13540
|
+
const repo = join(root, "..", "..", "..");
|
|
13007
13541
|
const pkg = files.get("package.json");
|
|
13008
|
-
if (
|
|
13009
|
-
|
|
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)));
|
|
13548
|
+
}
|
|
13549
|
+
return files;
|
|
13550
|
+
}
|
|
13551
|
+
const GITHUB_OWNER = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
|
|
13552
|
+
const GITHUB_REPO = /^[A-Za-z0-9._-]{1,100}$/;
|
|
13553
|
+
const PATH_SEGMENT = /^[A-Za-z0-9._@+-]{1,255}$/;
|
|
13554
|
+
const GIT_REF = /^[A-Za-z0-9._/+-]{1,255}$/;
|
|
13555
|
+
/** `--template` 값 → 받을 곳. 형식이 틀리면 api·네트워크를 부르기 전에 멈춘다 */
|
|
13556
|
+
function parseTemplateSpec(value) {
|
|
13557
|
+
const raw = value?.trim();
|
|
13558
|
+
if (!raw) return { kind: "bundled" };
|
|
13559
|
+
if (!raw.startsWith("github:")) return {
|
|
13560
|
+
kind: "registry",
|
|
13561
|
+
id: raw
|
|
13562
|
+
};
|
|
13563
|
+
const body = raw.slice(7);
|
|
13564
|
+
const hash = body.indexOf("#");
|
|
13565
|
+
const location = hash < 0 ? body : body.slice(0, hash);
|
|
13566
|
+
const ref = hash < 0 ? null : body.slice(hash + 1);
|
|
13567
|
+
const [owner = "", repo = "", ...rest] = location.split("/");
|
|
13568
|
+
if (!(GITHUB_OWNER.test(owner) && GITHUB_REPO.test(repo) && repo !== "." && repo !== ".." && rest.every((segment) => PATH_SEGMENT.test(segment) && segment !== "." && segment !== "..") && (ref === null || GIT_REF.test(ref) && !ref.split("/").some((s) => s === "" || s === "." || s === "..")))) throw new CliError(cli_create_template_invalid({ template: raw }));
|
|
13569
|
+
return {
|
|
13570
|
+
kind: "github",
|
|
13571
|
+
owner,
|
|
13572
|
+
repo,
|
|
13573
|
+
path: rest.join("/"),
|
|
13574
|
+
ref
|
|
13575
|
+
};
|
|
13576
|
+
}
|
|
13577
|
+
/** codeload tarball 주소 — ref가 없으면 기본 브랜치(`HEAD`) */
|
|
13578
|
+
function codeloadUrlOf(spec) {
|
|
13579
|
+
const ref = spec.ref ? spec.ref.split("/").map(encodeURIComponent).join("/") : "HEAD";
|
|
13580
|
+
return `https://codeload.github.com/${spec.owner}/${spec.repo}/tar.gz/${ref}`;
|
|
13581
|
+
}
|
|
13582
|
+
/** 받은 저장소 tarball을 풀어 둘 상한(압축 폭탄 방어선). 템플릿 저장소가 모노레포여도 정상 저장소는 닿지 않는다 */
|
|
13583
|
+
const TEMPLATE_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024;
|
|
13584
|
+
/** GitHub 템플릿 — tarball을 받아 맨 위 폴더(`{repo}-{sha}/`)를 벗기고 `경로` 아래만 남긴다 */
|
|
13585
|
+
async function githubTemplateFiles(spec, label, io) {
|
|
13586
|
+
let res;
|
|
13587
|
+
try {
|
|
13588
|
+
res = await io.fetch(codeloadUrlOf(spec));
|
|
13589
|
+
} catch {
|
|
13590
|
+
throw new CliError(cli_create_template_unreachable({ template: label }));
|
|
13591
|
+
}
|
|
13592
|
+
if (res.status === 404) throw new CliError(cli_create_template_github_not_found({ template: label }));
|
|
13593
|
+
if (!res.ok) throw new CliError(cli_create_template_download_failed({
|
|
13594
|
+
template: label,
|
|
13595
|
+
status: String(res.status)
|
|
13596
|
+
}));
|
|
13597
|
+
let entries;
|
|
13598
|
+
try {
|
|
13599
|
+
entries = readTar(await gunzip(new Uint8Array(await res.arrayBuffer()), TEMPLATE_ARCHIVE_MAX_BYTES), TEMPLATE_ARCHIVE_MAX_BYTES);
|
|
13600
|
+
} catch (error) {
|
|
13601
|
+
if (error instanceof TarLimitError) throw new CliError(cli_create_template_too_large({ template: label }));
|
|
13602
|
+
throw new CliError(cli_create_template_unreadable({ template: label }));
|
|
13603
|
+
}
|
|
13604
|
+
const prefix = spec.path ? `${spec.path}/` : "";
|
|
13605
|
+
const files = /* @__PURE__ */ new Map();
|
|
13606
|
+
for (const entry of entries) {
|
|
13607
|
+
const slash = entry.path.indexOf("/");
|
|
13608
|
+
if (slash < 0) continue;
|
|
13609
|
+
const inRepo = entry.path.slice(slash + 1);
|
|
13610
|
+
if (!inRepo.startsWith(prefix)) continue;
|
|
13611
|
+
const path = inRepo.slice(prefix.length);
|
|
13612
|
+
if (!path || path.startsWith("/") || path.split("/").some((s) => s === "" || s === "..")) continue;
|
|
13613
|
+
files.set(path, entry.bytes);
|
|
13614
|
+
}
|
|
13615
|
+
if (files.size === 0) throw new CliError(cli_create_template_path_empty({ template: label }));
|
|
13010
13616
|
return files;
|
|
13011
13617
|
}
|
|
13012
13618
|
/** npm 패키지 이름으로도 쓰는 폴더 이름 — 범위(@scope/) 없는 이름만 */
|
|
@@ -13017,24 +13623,33 @@ async function create(flags, io) {
|
|
|
13017
13623
|
if (!NAME_PATTERN.test(name)) throw new CliError(cli_create_name_invalid({ name }));
|
|
13018
13624
|
const target = resolve(io.cwd, name);
|
|
13019
13625
|
if (existsSync(target) && (!statSync(target).isDirectory() || readdirSync(target).length > 0)) throw new CliError(cli_create_dir_not_empty({ dir: name }));
|
|
13020
|
-
const storeCode =
|
|
13021
|
-
if (!storeCode) throw new CliError(
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
const
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13626
|
+
const storeCode = flags.storeCode?.trim();
|
|
13627
|
+
if (storeCode !== void 0 && !STORE_CODE_PATTERN.test(storeCode)) throw new CliError(cli_create_store_code_invalid({ storeCode }));
|
|
13628
|
+
const spec = parseTemplateSpec(flags.template);
|
|
13629
|
+
if (spec.kind === "registry") throw new CliError(cli_create_template_not_found({ template: spec.id }));
|
|
13630
|
+
const source = spec.kind === "github" ? await githubTemplateFiles(spec, flags.template?.trim() ?? "", io) : bundledStarterFiles(boilerplateRoot());
|
|
13631
|
+
let files;
|
|
13632
|
+
try {
|
|
13633
|
+
files = transformStarterFiles(source, { name });
|
|
13634
|
+
} catch (error) {
|
|
13635
|
+
if (error instanceof StarterTransformError) throw new CliError(cli_create_template_no_package_json({ reason: error.message }));
|
|
13636
|
+
throw error;
|
|
13029
13637
|
}
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13638
|
+
writeTree(target, files);
|
|
13639
|
+
if (storeCode) {
|
|
13640
|
+
const { apiOrigin } = resolveApiOrigin(flags, target, null, io.env);
|
|
13641
|
+
updateEnvFile(target, {
|
|
13642
|
+
SAYREN_API_URL: `${apiOrigin}/storefront/v1`,
|
|
13643
|
+
SAYREN_STORE_CODE: storeCode
|
|
13644
|
+
});
|
|
13645
|
+
io.out(cli_create_done_store_code({
|
|
13646
|
+
dir: name,
|
|
13647
|
+
count: files.size,
|
|
13648
|
+
storeCode
|
|
13649
|
+
}));
|
|
13650
|
+
} else io.out(cli_create_done({
|
|
13035
13651
|
dir: name,
|
|
13036
|
-
count: files.size
|
|
13037
|
-
storeCode
|
|
13652
|
+
count: files.size
|
|
13038
13653
|
}));
|
|
13039
13654
|
return 0;
|
|
13040
13655
|
}
|
|
@@ -13183,8 +13798,16 @@ async function main(argv, io) {
|
|
|
13183
13798
|
case "create": return create({
|
|
13184
13799
|
name: parsed.positional[0],
|
|
13185
13800
|
storeCode: str(parsed.flags["store-code"]),
|
|
13186
|
-
apiOrigin: str(parsed.flags["api-origin"])
|
|
13801
|
+
apiOrigin: str(parsed.flags["api-origin"]),
|
|
13802
|
+
template: str(parsed.flags.template)
|
|
13187
13803
|
}, io);
|
|
13804
|
+
case "link": return link(common, io);
|
|
13805
|
+
case "env":
|
|
13806
|
+
if (parsed.positional[0] !== "pull") throw new CliError(cli_env_usage());
|
|
13807
|
+
return envPull({
|
|
13808
|
+
...common,
|
|
13809
|
+
dir: str(parsed.flags.dir) ?? parsed.positional[1]
|
|
13810
|
+
}, io);
|
|
13188
13811
|
case "init": return init({
|
|
13189
13812
|
...common,
|
|
13190
13813
|
template: str(parsed.flags.template),
|