sayren 0.14.0 → 0.15.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/boilerplate/package.json +5 -5
- package/dist/index.mjs +816 -175
- package/package.json +3 -3
- 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);
|
|
@@ -5586,12 +5586,13 @@ const cli_create_dir_not_empty = ((inputs, options = {}) => {
|
|
|
5586
5586
|
//#endregion
|
|
5587
5587
|
//#region src/paraglide/messages/cli_create_done.js
|
|
5588
5588
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5589
|
-
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown
|
|
5589
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown> }} Cli_Create_DoneInputs */
|
|
5590
5590
|
const ko_cli_create_done = (i) => {
|
|
5591
|
-
return
|
|
5591
|
+
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개)
|
|
5592
5592
|
다음:
|
|
5593
5593
|
cd ${i?.dir}
|
|
5594
5594
|
npm install
|
|
5595
|
+
npx sayren link # 상점에 연결하고 .env에 상점 값을 씁니다
|
|
5595
5596
|
npm run dev`;
|
|
5596
5597
|
};
|
|
5597
5598
|
/**
|
|
@@ -5606,39 +5607,44 @@ const cli_create_done = ((inputs, options = {}) => {
|
|
|
5606
5607
|
});
|
|
5607
5608
|
|
|
5608
5609
|
//#endregion
|
|
5609
|
-
//#region src/paraglide/messages/
|
|
5610
|
+
//#region src/paraglide/messages/cli_create_done_store_code.js
|
|
5610
5611
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5611
|
-
/** @typedef {{
|
|
5612
|
-
const
|
|
5613
|
-
return
|
|
5612
|
+
/** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
|
|
5613
|
+
const ko_cli_create_done_store_code = (i) => {
|
|
5614
|
+
return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개, .env에 상점 ${i?.storeCode} 연결 값)
|
|
5615
|
+
다음:
|
|
5616
|
+
cd ${i?.dir}
|
|
5617
|
+
npm install
|
|
5618
|
+
npm run dev
|
|
5619
|
+
npx sayren deploy # 처음이면 상점 사이트에 연결한 뒤 배포합니다`;
|
|
5614
5620
|
};
|
|
5615
5621
|
/**
|
|
5616
5622
|
|
|
5617
|
-
* @param {
|
|
5623
|
+
* @param {Cli_Create_Done_Store_CodeInputs} inputs
|
|
5618
5624
|
* @param {{ locale?: "ko" }} options
|
|
5619
5625
|
* @returns {LocalizedString}
|
|
5620
5626
|
*/
|
|
5621
|
-
const
|
|
5627
|
+
const cli_create_done_store_code = ((inputs, options = {}) => {
|
|
5622
5628
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5623
|
-
return
|
|
5629
|
+
return ko_cli_create_done_store_code(inputs);
|
|
5624
5630
|
});
|
|
5625
5631
|
|
|
5626
5632
|
//#endregion
|
|
5627
|
-
//#region src/paraglide/messages/
|
|
5633
|
+
//#region src/paraglide/messages/cli_create_name_invalid.js
|
|
5628
5634
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5629
|
-
/** @typedef {{}}
|
|
5630
|
-
const
|
|
5631
|
-
return
|
|
5635
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_Create_Name_InvalidInputs */
|
|
5636
|
+
const ko_cli_create_name_invalid = (i) => {
|
|
5637
|
+
return `폴더 이름은 영문 소문자·숫자·.·_·-로 쓰고 영문 소문자나 숫자로 시작합니다: ${i?.name}`;
|
|
5632
5638
|
};
|
|
5633
5639
|
/**
|
|
5634
5640
|
|
|
5635
|
-
* @param {
|
|
5641
|
+
* @param {Cli_Create_Name_InvalidInputs} inputs
|
|
5636
5642
|
* @param {{ locale?: "ko" }} options
|
|
5637
5643
|
* @returns {LocalizedString}
|
|
5638
5644
|
*/
|
|
5639
|
-
const
|
|
5645
|
+
const cli_create_name_invalid = ((inputs, options = {}) => {
|
|
5640
5646
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5641
|
-
return
|
|
5647
|
+
return ko_cli_create_name_invalid(inputs);
|
|
5642
5648
|
});
|
|
5643
5649
|
|
|
5644
5650
|
//#endregion
|
|
@@ -5660,21 +5666,165 @@ const cli_create_store_code_invalid = ((inputs, options = {}) => {
|
|
|
5660
5666
|
});
|
|
5661
5667
|
|
|
5662
5668
|
//#endregion
|
|
5663
|
-
//#region src/paraglide/messages/
|
|
5669
|
+
//#region src/paraglide/messages/cli_create_template_download_failed.js
|
|
5664
5670
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5665
|
-
/** @typedef {{}}
|
|
5666
|
-
const
|
|
5667
|
-
return
|
|
5671
|
+
/** @typedef {{ template: NonNullable<unknown>, status: NonNullable<unknown> }} Cli_Create_Template_Download_FailedInputs */
|
|
5672
|
+
const ko_cli_create_template_download_failed = (i) => {
|
|
5673
|
+
return `템플릿을 받지 못했습니다: ${i?.template} (HTTP ${i?.status})`;
|
|
5668
5674
|
};
|
|
5669
5675
|
/**
|
|
5670
5676
|
|
|
5671
|
-
* @param {
|
|
5677
|
+
* @param {Cli_Create_Template_Download_FailedInputs} inputs
|
|
5672
5678
|
* @param {{ locale?: "ko" }} options
|
|
5673
5679
|
* @returns {LocalizedString}
|
|
5674
5680
|
*/
|
|
5675
|
-
const
|
|
5681
|
+
const cli_create_template_download_failed = ((inputs, options = {}) => {
|
|
5676
5682
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5677
|
-
return
|
|
5683
|
+
return ko_cli_create_template_download_failed(inputs);
|
|
5684
|
+
});
|
|
5685
|
+
|
|
5686
|
+
//#endregion
|
|
5687
|
+
//#region src/paraglide/messages/cli_create_template_github_not_found.js
|
|
5688
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5689
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Github_Not_FoundInputs */
|
|
5690
|
+
const ko_cli_create_template_github_not_found = (i) => {
|
|
5691
|
+
return `GitHub에서 템플릿을 찾을 수 없습니다: ${i?.template}. 공개 저장소·ref를 확인하십시오`;
|
|
5692
|
+
};
|
|
5693
|
+
/**
|
|
5694
|
+
|
|
5695
|
+
* @param {Cli_Create_Template_Github_Not_FoundInputs} inputs
|
|
5696
|
+
* @param {{ locale?: "ko" }} options
|
|
5697
|
+
* @returns {LocalizedString}
|
|
5698
|
+
*/
|
|
5699
|
+
const cli_create_template_github_not_found = ((inputs, options = {}) => {
|
|
5700
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5701
|
+
return ko_cli_create_template_github_not_found(inputs);
|
|
5702
|
+
});
|
|
5703
|
+
|
|
5704
|
+
//#endregion
|
|
5705
|
+
//#region src/paraglide/messages/cli_create_template_invalid.js
|
|
5706
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5707
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_InvalidInputs */
|
|
5708
|
+
const ko_cli_create_template_invalid = (i) => {
|
|
5709
|
+
return `템플릿 형식이 아닙니다: ${i?.template}. github:owner/repo[/경로][#ref] 또는 템플릿 id를 주십시오`;
|
|
5710
|
+
};
|
|
5711
|
+
/**
|
|
5712
|
+
|
|
5713
|
+
* @param {Cli_Create_Template_InvalidInputs} inputs
|
|
5714
|
+
* @param {{ locale?: "ko" }} options
|
|
5715
|
+
* @returns {LocalizedString}
|
|
5716
|
+
*/
|
|
5717
|
+
const cli_create_template_invalid = ((inputs, options = {}) => {
|
|
5718
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5719
|
+
return ko_cli_create_template_invalid(inputs);
|
|
5720
|
+
});
|
|
5721
|
+
|
|
5722
|
+
//#endregion
|
|
5723
|
+
//#region src/paraglide/messages/cli_create_template_no_package_json.js
|
|
5724
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5725
|
+
/** @typedef {{ reason: NonNullable<unknown> }} Cli_Create_Template_No_Package_JsonInputs */
|
|
5726
|
+
const ko_cli_create_template_no_package_json = (i) => {
|
|
5727
|
+
return `시작 코드로 쓸 수 없는 템플릿입니다(${i?.reason})`;
|
|
5728
|
+
};
|
|
5729
|
+
/**
|
|
5730
|
+
|
|
5731
|
+
* @param {Cli_Create_Template_No_Package_JsonInputs} inputs
|
|
5732
|
+
* @param {{ locale?: "ko" }} options
|
|
5733
|
+
* @returns {LocalizedString}
|
|
5734
|
+
*/
|
|
5735
|
+
const cli_create_template_no_package_json = ((inputs, options = {}) => {
|
|
5736
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5737
|
+
return ko_cli_create_template_no_package_json(inputs);
|
|
5738
|
+
});
|
|
5739
|
+
|
|
5740
|
+
//#endregion
|
|
5741
|
+
//#region src/paraglide/messages/cli_create_template_not_found.js
|
|
5742
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5743
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Not_FoundInputs */
|
|
5744
|
+
const ko_cli_create_template_not_found = (i) => {
|
|
5745
|
+
return `템플릿을 찾을 수 없습니다: ${i?.template}`;
|
|
5746
|
+
};
|
|
5747
|
+
/**
|
|
5748
|
+
|
|
5749
|
+
* @param {Cli_Create_Template_Not_FoundInputs} inputs
|
|
5750
|
+
* @param {{ locale?: "ko" }} options
|
|
5751
|
+
* @returns {LocalizedString}
|
|
5752
|
+
*/
|
|
5753
|
+
const cli_create_template_not_found = ((inputs, options = {}) => {
|
|
5754
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5755
|
+
return ko_cli_create_template_not_found(inputs);
|
|
5756
|
+
});
|
|
5757
|
+
|
|
5758
|
+
//#endregion
|
|
5759
|
+
//#region src/paraglide/messages/cli_create_template_path_empty.js
|
|
5760
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5761
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Path_EmptyInputs */
|
|
5762
|
+
const ko_cli_create_template_path_empty = (i) => {
|
|
5763
|
+
return `템플릿 경로에 파일이 없습니다: ${i?.template}`;
|
|
5764
|
+
};
|
|
5765
|
+
/**
|
|
5766
|
+
|
|
5767
|
+
* @param {Cli_Create_Template_Path_EmptyInputs} inputs
|
|
5768
|
+
* @param {{ locale?: "ko" }} options
|
|
5769
|
+
* @returns {LocalizedString}
|
|
5770
|
+
*/
|
|
5771
|
+
const cli_create_template_path_empty = ((inputs, options = {}) => {
|
|
5772
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5773
|
+
return ko_cli_create_template_path_empty(inputs);
|
|
5774
|
+
});
|
|
5775
|
+
|
|
5776
|
+
//#endregion
|
|
5777
|
+
//#region src/paraglide/messages/cli_create_template_too_large.js
|
|
5778
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5779
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_Too_LargeInputs */
|
|
5780
|
+
const ko_cli_create_template_too_large = (i) => {
|
|
5781
|
+
return `템플릿 저장소가 너무 커서 받지 않았습니다: ${i?.template}`;
|
|
5782
|
+
};
|
|
5783
|
+
/**
|
|
5784
|
+
|
|
5785
|
+
* @param {Cli_Create_Template_Too_LargeInputs} inputs
|
|
5786
|
+
* @param {{ locale?: "ko" }} options
|
|
5787
|
+
* @returns {LocalizedString}
|
|
5788
|
+
*/
|
|
5789
|
+
const cli_create_template_too_large = ((inputs, options = {}) => {
|
|
5790
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5791
|
+
return ko_cli_create_template_too_large(inputs);
|
|
5792
|
+
});
|
|
5793
|
+
|
|
5794
|
+
//#endregion
|
|
5795
|
+
//#region src/paraglide/messages/cli_create_template_unreachable.js
|
|
5796
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5797
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_UnreachableInputs */
|
|
5798
|
+
const ko_cli_create_template_unreachable = (i) => {
|
|
5799
|
+
return `템플릿을 받지 못했습니다: ${i?.template}. 네트워크 연결을 확인하십시오`;
|
|
5800
|
+
};
|
|
5801
|
+
/**
|
|
5802
|
+
|
|
5803
|
+
* @param {Cli_Create_Template_UnreachableInputs} inputs
|
|
5804
|
+
* @param {{ locale?: "ko" }} options
|
|
5805
|
+
* @returns {LocalizedString}
|
|
5806
|
+
*/
|
|
5807
|
+
const cli_create_template_unreachable = ((inputs, options = {}) => {
|
|
5808
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5809
|
+
return ko_cli_create_template_unreachable(inputs);
|
|
5810
|
+
});
|
|
5811
|
+
|
|
5812
|
+
//#endregion
|
|
5813
|
+
//#region src/paraglide/messages/cli_create_template_unreadable.js
|
|
5814
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5815
|
+
/** @typedef {{ template: NonNullable<unknown> }} Cli_Create_Template_UnreadableInputs */
|
|
5816
|
+
const ko_cli_create_template_unreadable = (i) => {
|
|
5817
|
+
return `템플릿 압축 파일을 읽지 못했습니다: ${i?.template}`;
|
|
5818
|
+
};
|
|
5819
|
+
/**
|
|
5820
|
+
|
|
5821
|
+
* @param {Cli_Create_Template_UnreadableInputs} inputs
|
|
5822
|
+
* @param {{ locale?: "ko" }} options
|
|
5823
|
+
* @returns {LocalizedString}
|
|
5824
|
+
*/
|
|
5825
|
+
const cli_create_template_unreadable = ((inputs, options = {}) => {
|
|
5826
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
5827
|
+
return ko_cli_create_template_unreadable(inputs);
|
|
5678
5828
|
});
|
|
5679
5829
|
|
|
5680
5830
|
//#endregion
|
|
@@ -5682,7 +5832,7 @@ const cli_create_store_code_required = ((inputs = {}, options = {}) => {
|
|
|
5682
5832
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5683
5833
|
/** @typedef {{}} Cli_Create_UsageInputs */
|
|
5684
5834
|
const ko_cli_create_usage = () => {
|
|
5685
|
-
return `폴더 이름을 주십시오: npx sayren create <이름> [--
|
|
5835
|
+
return `폴더 이름을 주십시오: npx sayren create <이름> [--template <템플릿>]`;
|
|
5686
5836
|
};
|
|
5687
5837
|
/**
|
|
5688
5838
|
|
|
@@ -6111,40 +6261,40 @@ const cli_deploy_force_yes_required = ((inputs = {}, options = {}) => {
|
|
|
6111
6261
|
});
|
|
6112
6262
|
|
|
6113
6263
|
//#endregion
|
|
6114
|
-
//#region src/paraglide/messages/
|
|
6264
|
+
//#region src/paraglide/messages/cli_deploy_linking.js
|
|
6115
6265
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6116
|
-
/** @typedef {{}}
|
|
6117
|
-
const
|
|
6118
|
-
return
|
|
6119
|
-
· 병합된 트리를 이 폴더에 받으려면: npx sayren pull --force`;
|
|
6266
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Deploy_LinkingInputs */
|
|
6267
|
+
const ko_cli_deploy_linking = (i) => {
|
|
6268
|
+
return `${i?.dir}는 아직 상점에 연결되지 않았습니다 — 먼저 연결합니다(npx sayren link)`;
|
|
6120
6269
|
};
|
|
6121
6270
|
/**
|
|
6122
6271
|
|
|
6123
|
-
* @param {
|
|
6272
|
+
* @param {Cli_Deploy_LinkingInputs} inputs
|
|
6124
6273
|
* @param {{ locale?: "ko" }} options
|
|
6125
6274
|
* @returns {LocalizedString}
|
|
6126
6275
|
*/
|
|
6127
|
-
const
|
|
6276
|
+
const cli_deploy_linking = ((inputs, options = {}) => {
|
|
6128
6277
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6129
|
-
return
|
|
6278
|
+
return ko_cli_deploy_linking(inputs);
|
|
6130
6279
|
});
|
|
6131
6280
|
|
|
6132
6281
|
//#endregion
|
|
6133
|
-
//#region src/paraglide/messages/
|
|
6282
|
+
//#region src/paraglide/messages/cli_deploy_merged.js
|
|
6134
6283
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6135
|
-
/** @typedef {{
|
|
6136
|
-
const
|
|
6137
|
-
return
|
|
6284
|
+
/** @typedef {{}} Cli_Deploy_MergedInputs */
|
|
6285
|
+
const ko_cli_deploy_merged = () => {
|
|
6286
|
+
return `pull 이후 원격 작업 트리가 바뀌어(상점 플랫폼·AI 편집) 서버가 병합했습니다. 원격 변경은 그대로 남습니다.
|
|
6287
|
+
· 병합된 트리를 이 폴더에 받으려면: npx sayren pull --force`;
|
|
6138
6288
|
};
|
|
6139
6289
|
/**
|
|
6140
6290
|
|
|
6141
|
-
* @param {
|
|
6291
|
+
* @param {Cli_Deploy_MergedInputs} inputs
|
|
6142
6292
|
* @param {{ locale?: "ko" }} options
|
|
6143
6293
|
* @returns {LocalizedString}
|
|
6144
6294
|
*/
|
|
6145
|
-
const
|
|
6295
|
+
const cli_deploy_merged = ((inputs = {}, options = {}) => {
|
|
6146
6296
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6147
|
-
return
|
|
6297
|
+
return ko_cli_deploy_merged(inputs);
|
|
6148
6298
|
});
|
|
6149
6299
|
|
|
6150
6300
|
//#endregion
|
|
@@ -6489,6 +6639,78 @@ const cli_edit_session_open = ((inputs = {}, options = {}) => {
|
|
|
6489
6639
|
return ko_cli_edit_session_open(inputs);
|
|
6490
6640
|
});
|
|
6491
6641
|
|
|
6642
|
+
//#endregion
|
|
6643
|
+
//#region src/paraglide/messages/cli_env_not_linked.js
|
|
6644
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6645
|
+
/** @typedef {{ file: NonNullable<unknown> }} Cli_Env_Not_LinkedInputs */
|
|
6646
|
+
const ko_cli_env_not_linked = (i) => {
|
|
6647
|
+
return `이 폴더는 상점에 연결되지 않았습니다(${i?.file} 없음). npx sayren link로 먼저 연결하십시오`;
|
|
6648
|
+
};
|
|
6649
|
+
/**
|
|
6650
|
+
|
|
6651
|
+
* @param {Cli_Env_Not_LinkedInputs} inputs
|
|
6652
|
+
* @param {{ locale?: "ko" }} options
|
|
6653
|
+
* @returns {LocalizedString}
|
|
6654
|
+
*/
|
|
6655
|
+
const cli_env_not_linked = ((inputs, options = {}) => {
|
|
6656
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6657
|
+
return ko_cli_env_not_linked(inputs);
|
|
6658
|
+
});
|
|
6659
|
+
|
|
6660
|
+
//#endregion
|
|
6661
|
+
//#region src/paraglide/messages/cli_env_pulled.js
|
|
6662
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6663
|
+
/** @typedef {{ file: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Env_PulledInputs */
|
|
6664
|
+
const ko_cli_env_pulled = (i) => {
|
|
6665
|
+
return `${i?.file}에 상점 ${i?.storeCode} 연결 값(SAYREN_API_URL·SAYREN_STORE_CODE)을 썼습니다`;
|
|
6666
|
+
};
|
|
6667
|
+
/**
|
|
6668
|
+
|
|
6669
|
+
* @param {Cli_Env_PulledInputs} inputs
|
|
6670
|
+
* @param {{ locale?: "ko" }} options
|
|
6671
|
+
* @returns {LocalizedString}
|
|
6672
|
+
*/
|
|
6673
|
+
const cli_env_pulled = ((inputs, options = {}) => {
|
|
6674
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6675
|
+
return ko_cli_env_pulled(inputs);
|
|
6676
|
+
});
|
|
6677
|
+
|
|
6678
|
+
//#endregion
|
|
6679
|
+
//#region src/paraglide/messages/cli_env_store_code_unknown.js
|
|
6680
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6681
|
+
/** @typedef {{}} Cli_Env_Store_Code_UnknownInputs */
|
|
6682
|
+
const ko_cli_env_store_code_unknown = () => {
|
|
6683
|
+
return `상점 코드를 읽지 못했습니다. 로그인(npx sayren login)을 다시 하거나 토큰에 stores:read 권한이 있는지 확인하십시오`;
|
|
6684
|
+
};
|
|
6685
|
+
/**
|
|
6686
|
+
|
|
6687
|
+
* @param {Cli_Env_Store_Code_UnknownInputs} inputs
|
|
6688
|
+
* @param {{ locale?: "ko" }} options
|
|
6689
|
+
* @returns {LocalizedString}
|
|
6690
|
+
*/
|
|
6691
|
+
const cli_env_store_code_unknown = ((inputs = {}, options = {}) => {
|
|
6692
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6693
|
+
return ko_cli_env_store_code_unknown(inputs);
|
|
6694
|
+
});
|
|
6695
|
+
|
|
6696
|
+
//#endregion
|
|
6697
|
+
//#region src/paraglide/messages/cli_env_usage.js
|
|
6698
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6699
|
+
/** @typedef {{}} Cli_Env_UsageInputs */
|
|
6700
|
+
const ko_cli_env_usage = () => {
|
|
6701
|
+
return `사용법: npx sayren env pull [--dir <폴더>]`;
|
|
6702
|
+
};
|
|
6703
|
+
/**
|
|
6704
|
+
|
|
6705
|
+
* @param {Cli_Env_UsageInputs} inputs
|
|
6706
|
+
* @param {{ locale?: "ko" }} options
|
|
6707
|
+
* @returns {LocalizedString}
|
|
6708
|
+
*/
|
|
6709
|
+
const cli_env_usage = ((inputs = {}, options = {}) => {
|
|
6710
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6711
|
+
return ko_cli_env_usage(inputs);
|
|
6712
|
+
});
|
|
6713
|
+
|
|
6492
6714
|
//#endregion
|
|
6493
6715
|
//#region src/paraglide/messages/cli_error_api_unreachable.js
|
|
6494
6716
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -7030,6 +7252,135 @@ const cli_legacy_rc_tracked = ((inputs, options = {}) => {
|
|
|
7030
7252
|
return ko_cli_legacy_rc_tracked(inputs);
|
|
7031
7253
|
});
|
|
7032
7254
|
|
|
7255
|
+
//#endregion
|
|
7256
|
+
//#region src/paraglide/messages/cli_link_dir_missing.js
|
|
7257
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7258
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Link_Dir_MissingInputs */
|
|
7259
|
+
const ko_cli_link_dir_missing = (i) => {
|
|
7260
|
+
return `폴더가 없습니다: ${i?.dir}`;
|
|
7261
|
+
};
|
|
7262
|
+
/**
|
|
7263
|
+
|
|
7264
|
+
* @param {Cli_Link_Dir_MissingInputs} inputs
|
|
7265
|
+
* @param {{ locale?: "ko" }} options
|
|
7266
|
+
* @returns {LocalizedString}
|
|
7267
|
+
*/
|
|
7268
|
+
const cli_link_dir_missing = ((inputs, options = {}) => {
|
|
7269
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7270
|
+
return ko_cli_link_dir_missing(inputs);
|
|
7271
|
+
});
|
|
7272
|
+
|
|
7273
|
+
//#endregion
|
|
7274
|
+
//#region src/paraglide/messages/cli_link_done.js
|
|
7275
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7276
|
+
/** @typedef {{ storeId: NonNullable<unknown>, url: NonNullable<unknown>, file: NonNullable<unknown>, storeCode: NonNullable<unknown>, envFile: NonNullable<unknown> }} Cli_Link_DoneInputs */
|
|
7277
|
+
const ko_cli_link_done = (i) => {
|
|
7278
|
+
return `연결했습니다 — 상점 ${i?.storeId}, 사이트 ${i?.url}. 연결 정보는 ${i?.file}, 상점 ${i?.storeCode} 연결 값은 ${i?.envFile}에 두었습니다`;
|
|
7279
|
+
};
|
|
7280
|
+
/**
|
|
7281
|
+
|
|
7282
|
+
* @param {Cli_Link_DoneInputs} inputs
|
|
7283
|
+
* @param {{ locale?: "ko" }} options
|
|
7284
|
+
* @returns {LocalizedString}
|
|
7285
|
+
*/
|
|
7286
|
+
const cli_link_done = ((inputs, options = {}) => {
|
|
7287
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7288
|
+
return ko_cli_link_done(inputs);
|
|
7289
|
+
});
|
|
7290
|
+
|
|
7291
|
+
//#endregion
|
|
7292
|
+
//#region src/paraglide/messages/cli_link_editing_disabled.js
|
|
7293
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7294
|
+
/** @typedef {{}} Cli_Link_Editing_DisabledInputs */
|
|
7295
|
+
const ko_cli_link_editing_disabled = () => {
|
|
7296
|
+
return `지금은 스토어프론트를 연결할 수 없습니다(편집 기능이 꺼져 있습니다)`;
|
|
7297
|
+
};
|
|
7298
|
+
/**
|
|
7299
|
+
|
|
7300
|
+
* @param {Cli_Link_Editing_DisabledInputs} inputs
|
|
7301
|
+
* @param {{ locale?: "ko" }} options
|
|
7302
|
+
* @returns {LocalizedString}
|
|
7303
|
+
*/
|
|
7304
|
+
const cli_link_editing_disabled = ((inputs = {}, options = {}) => {
|
|
7305
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7306
|
+
return ko_cli_link_editing_disabled(inputs);
|
|
7307
|
+
});
|
|
7308
|
+
|
|
7309
|
+
//#endregion
|
|
7310
|
+
//#region src/paraglide/messages/cli_link_gitignore.js
|
|
7311
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7312
|
+
/** @typedef {{ lines: NonNullable<unknown> }} Cli_Link_GitignoreInputs */
|
|
7313
|
+
const ko_cli_link_gitignore = (i) => {
|
|
7314
|
+
return `.gitignore에 ${i?.lines}를 더했습니다(배포에 올리지 않습니다)`;
|
|
7315
|
+
};
|
|
7316
|
+
/**
|
|
7317
|
+
|
|
7318
|
+
* @param {Cli_Link_GitignoreInputs} inputs
|
|
7319
|
+
* @param {{ locale?: "ko" }} options
|
|
7320
|
+
* @returns {LocalizedString}
|
|
7321
|
+
*/
|
|
7322
|
+
const cli_link_gitignore = ((inputs, options = {}) => {
|
|
7323
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7324
|
+
return ko_cli_link_gitignore(inputs);
|
|
7325
|
+
});
|
|
7326
|
+
|
|
7327
|
+
//#endregion
|
|
7328
|
+
//#region src/paraglide/messages/cli_link_next_steps.js
|
|
7329
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7330
|
+
/** @typedef {{}} Cli_Link_Next_StepsInputs */
|
|
7331
|
+
const ko_cli_link_next_steps = () => {
|
|
7332
|
+
return `다음:
|
|
7333
|
+
npm install
|
|
7334
|
+
npm run dev # http://localhost:4010 — 운영 상점 데이터로 동작합니다
|
|
7335
|
+
npx sayren deploy # 빌드 → 버전 → 발행`;
|
|
7336
|
+
};
|
|
7337
|
+
/**
|
|
7338
|
+
|
|
7339
|
+
* @param {Cli_Link_Next_StepsInputs} inputs
|
|
7340
|
+
* @param {{ locale?: "ko" }} options
|
|
7341
|
+
* @returns {LocalizedString}
|
|
7342
|
+
*/
|
|
7343
|
+
const cli_link_next_steps = ((inputs = {}, options = {}) => {
|
|
7344
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7345
|
+
return ko_cli_link_next_steps(inputs);
|
|
7346
|
+
});
|
|
7347
|
+
|
|
7348
|
+
//#endregion
|
|
7349
|
+
//#region src/paraglide/messages/cli_link_site_created.js
|
|
7350
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7351
|
+
/** @typedef {{ url: NonNullable<unknown> }} Cli_Link_Site_CreatedInputs */
|
|
7352
|
+
const ko_cli_link_site_created = (i) => {
|
|
7353
|
+
return `호스팅 사이트를 만들었습니다 — ${i?.url} (오픈 준비 중, 첫 배포가 성공하면 발행합니다)`;
|
|
7354
|
+
};
|
|
7355
|
+
/**
|
|
7356
|
+
|
|
7357
|
+
* @param {Cli_Link_Site_CreatedInputs} inputs
|
|
7358
|
+
* @param {{ locale?: "ko" }} options
|
|
7359
|
+
* @returns {LocalizedString}
|
|
7360
|
+
*/
|
|
7361
|
+
const cli_link_site_created = ((inputs, options = {}) => {
|
|
7362
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7363
|
+
return ko_cli_link_site_created(inputs);
|
|
7364
|
+
});
|
|
7365
|
+
|
|
7366
|
+
//#endregion
|
|
7367
|
+
//#region src/paraglide/messages/cli_link_slug_taken.js
|
|
7368
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7369
|
+
/** @typedef {{}} Cli_Link_Slug_TakenInputs */
|
|
7370
|
+
const ko_cli_link_slug_taken = () => {
|
|
7371
|
+
return `상점 코드와 같은 주소를 다른 사이트가 쓰고 있어 사이트를 만들지 못했습니다. 상점 플랫폼 › 스토어프론트에서 다른 주소로 만든 뒤 npx sayren link로 다시 연결하십시오`;
|
|
7372
|
+
};
|
|
7373
|
+
/**
|
|
7374
|
+
|
|
7375
|
+
* @param {Cli_Link_Slug_TakenInputs} inputs
|
|
7376
|
+
* @param {{ locale?: "ko" }} options
|
|
7377
|
+
* @returns {LocalizedString}
|
|
7378
|
+
*/
|
|
7379
|
+
const cli_link_slug_taken = ((inputs = {}, options = {}) => {
|
|
7380
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7381
|
+
return ko_cli_link_slug_taken(inputs);
|
|
7382
|
+
});
|
|
7383
|
+
|
|
7033
7384
|
//#endregion
|
|
7034
7385
|
//#region src/paraglide/messages/cli_login_browser_prompt.js
|
|
7035
7386
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -8413,7 +8764,7 @@ const cli_state_invalid = ((inputs, options = {}) => {
|
|
|
8413
8764
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8414
8765
|
/** @typedef {{ name: NonNullable<unknown> }} Cli_State_Missing_FieldsInputs */
|
|
8415
8766
|
const ko_cli_state_missing_fields = (i) => {
|
|
8416
|
-
return `${i?.name}에 storeId·siteId·baseRef가 없습니다. npx sayren
|
|
8767
|
+
return `${i?.name}에 storeId·siteId·baseRef가 없습니다. npx sayren link로 다시 연결하십시오`;
|
|
8417
8768
|
};
|
|
8418
8769
|
/**
|
|
8419
8770
|
|
|
@@ -8534,6 +8885,24 @@ const cli_status_folder_base = ((inputs, options = {}) => {
|
|
|
8534
8885
|
return ko_cli_status_folder_base(inputs);
|
|
8535
8886
|
});
|
|
8536
8887
|
|
|
8888
|
+
//#endregion
|
|
8889
|
+
//#region src/paraglide/messages/cli_status_folder_linked_only.js
|
|
8890
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8891
|
+
/** @typedef {{}} Cli_Status_Folder_Linked_OnlyInputs */
|
|
8892
|
+
const ko_cli_status_folder_linked_only = () => {
|
|
8893
|
+
return `이 폴더: 연결만 했습니다(아직 올리지 않음 — 다음 deploy가 교체합니다)`;
|
|
8894
|
+
};
|
|
8895
|
+
/**
|
|
8896
|
+
|
|
8897
|
+
* @param {Cli_Status_Folder_Linked_OnlyInputs} inputs
|
|
8898
|
+
* @param {{ locale?: "ko" }} options
|
|
8899
|
+
* @returns {LocalizedString}
|
|
8900
|
+
*/
|
|
8901
|
+
const cli_status_folder_linked_only = ((inputs = {}, options = {}) => {
|
|
8902
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8903
|
+
return ko_cli_status_folder_linked_only(inputs);
|
|
8904
|
+
});
|
|
8905
|
+
|
|
8537
8906
|
//#endregion
|
|
8538
8907
|
//#region src/paraglide/messages/cli_status_folder_other_site.js
|
|
8539
8908
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -9001,7 +9370,9 @@ const ko_cli_usage = (i) => {
|
|
|
9001
9370
|
npx sayren login --paste # 액세스 토큰을 입력받아 저장(셸 기록에 남지 않습니다)
|
|
9002
9371
|
npx sayren logout
|
|
9003
9372
|
npx sayren whoami
|
|
9004
|
-
npx sayren create <이름> [--
|
|
9373
|
+
npx sayren create <이름> [--template <템플릿>] # 시작 코드를 새 폴더에 만듭니다(파일만, 로그인 없음)
|
|
9374
|
+
npx sayren link [폴더] [--store <storeId>] # 폴더를 상점 사이트에 연결합니다(없으면 사이트를 만듭니다)
|
|
9375
|
+
npx sayren env pull # 연결된 상점 값을 .env에 씁니다
|
|
9005
9376
|
npx sayren init [폴더] [--template <id>] [--store <storeId>] [--yes]
|
|
9006
9377
|
npx sayren pull --project <storeId> [폴더] [--force [--yes]]
|
|
9007
9378
|
npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
|
|
@@ -9014,10 +9385,12 @@ const ko_cli_usage = (i) => {
|
|
|
9014
9385
|
login Sayren 계정으로 로그인합니다. 로그인 토큰은 사용자 설정 폴더(~/.config/sayren/credentials.json)에 둡니다
|
|
9015
9386
|
logout 로그인을 폐기하고 이 컴퓨터의 토큰을 지웁니다
|
|
9016
9387
|
whoami 쓰는 계정·토큰 종류·상점 범위
|
|
9017
|
-
create
|
|
9388
|
+
create 시작 코드(상점 이름·상품 목록 홈, SDK·문구·분석·에이전트 안내)를 <이름> 폴더에 만듭니다. 상점을 묻지 않고 호스팅 사이트도 만들지 않습니다
|
|
9389
|
+
link 상점을 고르고(--store → 토큰 범위 → 선택) 사이트가 없으면 만든 뒤 .sayren/project.json·.env·.mcp.json을 씁니다
|
|
9390
|
+
env env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL을 .env에 씁니다
|
|
9018
9391
|
init 템플릿을 골라 호스팅 사이트를 만들고 pull과 같은 폴더(기본: 상점 코드)로 받습니다. 사이트가 이미 있으면 만들지 않습니다
|
|
9019
|
-
pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/
|
|
9020
|
-
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 원격이
|
|
9392
|
+
pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/project.json을 씁니다
|
|
9393
|
+
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 연결이 없으면 link부터 하고 다음 버전으로 교체합니다. 원격이 마지막 받기·올리기 이후 바뀌었으면 덮어쓰지 않습니다
|
|
9021
9394
|
status 사이트·발행 버전·최근 빌드·편집 환경
|
|
9022
9395
|
verify 로컬 규칙·경로·크기 검사(서버 빌드와 같은 규칙)
|
|
9023
9396
|
skill sayren 스킬 목록(list)과 본문(<이름>)을 출력합니다. 스킬은 중앙에서 고치므로 늘 최신입니다
|
|
@@ -9030,8 +9403,9 @@ const ko_cli_usage = (i) => {
|
|
|
9030
9403
|
|
|
9031
9404
|
옵션:
|
|
9032
9405
|
--project <storeId> pull: 받을 상점(--store와 같습니다)
|
|
9033
|
-
--template
|
|
9034
|
-
|
|
9406
|
+
--template <템플릿> create: github:owner/repo[/경로][#ref](공개 GitHub 저장소) 또는 템플릿 id. 없으면 CLI에 든 시작 코드
|
|
9407
|
+
init: 호스팅 템플릿 id(비대화형은 필수)
|
|
9408
|
+
--store-code <코드> create: 이 상점 코드로 .env를 씁니다(없으면 link·env pull이 씁니다)
|
|
9035
9409
|
--dir <폴더> 작업 폴더(기본: 지금 폴더, pull·init은 상점 코드)
|
|
9036
9410
|
--force pull: 같은 사이트를 받은 폴더에 원격을 다시 받습니다(지울·덮을 파일을 보여 주고 묻습니다)
|
|
9037
9411
|
deploy: 원격 변경·열린 편집 환경을 무시하고 덮어씁니다(확인을 묻습니다)
|
|
@@ -9234,8 +9608,9 @@ function useProcessLocale(locale) {
|
|
|
9234
9608
|
/**
|
|
9235
9609
|
* 설정 (2026-09-29 사용자 결정: `.sayrenrc`를 없애고 받은 폴더에 원격 MCP `.mcp.json`을 둔다)
|
|
9236
9610
|
*
|
|
9237
|
-
* - `.sayren/state.json
|
|
9238
|
-
* 올리지 않는다. `baseRef`가 deploy의 `expectedBaseRef`다(원격이 pull 이후 바뀌었으면 `SOURCE_CHANGED`).
|
|
9611
|
+
* - `.sayren/project.json`(#315 C6, 옛 `.sayren/state.json`): link·pull·deploy가 쓰는 연결 정보(`storeId`·`siteId`·`baseRef`, 기본이 아닌 api 주소).
|
|
9612
|
+
* `.gitignore` 대상이고 올리지 않는다. `baseRef`가 deploy의 `expectedBaseRef`다(원격이 pull 이후 바뀌었으면 `SOURCE_CHANGED`). `link`가 막
|
|
9613
|
+
* 연결한 폴더는 `baseRef`가 null이다 — 첫 deploy가 조건 없이 다음 버전으로 교체하고(머리 없는 사이트는 첫 커밋), 그 뒤부터 올린 트리가 기준이다.
|
|
9239
9614
|
* - `.mcp.json`: Claude Code 형식의 `sayren` 원격 MCP 항목. 주소와 `X-Store-Id`만 있고 토큰은 없다(#71 — MCP 클라이언트가
|
|
9240
9615
|
* 연결할 때 스스로 로그인한다). 옛 폴더의 폐기된 토큰(`sy_pat_…`) 헤더는 쓸 때 지운다. `.gitignore` 대상이고 올리지 않는다.
|
|
9241
9616
|
* - `.env`: 스토어프론트 템플릿 개발 서버가 읽는 값(`SAYREN_API_URL`·`SAYREN_STORE_CODE`)만 둔다. 토큰을 두지 않는다.
|
|
@@ -9245,10 +9620,12 @@ function useProcessLocale(locale) {
|
|
|
9245
9620
|
* 옛 API 토큰(`sy_pat_…`)은 R1에서 폐기되어 api에 보내지 않고 거절한다(`cli_retired_token`).
|
|
9246
9621
|
* api 주소는 `--api-origin` → `SAYREN_API_ORIGIN` → `.env`의
|
|
9247
9622
|
* `SAYREN_API_URL` → 상태 파일 → 기본값 순서이고, 파일에서 온 기본이 아닌 주소는 확인한다(`checkAuthSources`).
|
|
9248
|
-
* 옛 폴더(`.sayrenrc`, `.env`의 `SAYREN_TOKEN`)는 한 번 정리한다(`migrateLegacy`).
|
|
9623
|
+
* 옛 폴더(`.sayrenrc`, `.sayren/state.json`, `.env`의 `SAYREN_TOKEN`)는 한 번 정리한다(`migrateLegacy`).
|
|
9249
9624
|
*/
|
|
9250
9625
|
const STATE_DIR = ".sayren";
|
|
9251
|
-
const STATE_FILE = `${STATE_DIR}/
|
|
9626
|
+
const STATE_FILE = `${STATE_DIR}/project.json`;
|
|
9627
|
+
/** 옛 연결 파일(0.2.0 ~ 0.14.x) — 읽고, 옮긴 뒤 지운다 */
|
|
9628
|
+
const LEGACY_STATE_FILE = `${STATE_DIR}/state.json`;
|
|
9252
9629
|
/** 옛 상태 파일(0.1.x) — 읽어서 옮기고 지운다 */
|
|
9253
9630
|
const LEGACY_RC_FILE = ".sayrenrc";
|
|
9254
9631
|
const ENV_FILE = ".env";
|
|
@@ -9263,7 +9640,7 @@ function parseState(text, name) {
|
|
|
9263
9640
|
}
|
|
9264
9641
|
if (!parsed || typeof parsed !== "object") throw new CliError(cli_state_invalid({ name }));
|
|
9265
9642
|
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 }));
|
|
9643
|
+
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
9644
|
return {
|
|
9268
9645
|
storeId: raw.storeId,
|
|
9269
9646
|
siteId: raw.siteId,
|
|
@@ -9271,10 +9648,13 @@ function parseState(text, name) {
|
|
|
9271
9648
|
...typeof raw.apiOrigin === "string" ? { apiOrigin: raw.apiOrigin } : {}
|
|
9272
9649
|
};
|
|
9273
9650
|
}
|
|
9651
|
+
/** 연결 정보 — `project.json`, 없으면 옛 `state.json`을 읽는다(옮기기는 `migrateLegacy`) */
|
|
9274
9652
|
function readState(dir) {
|
|
9275
|
-
const
|
|
9276
|
-
|
|
9277
|
-
|
|
9653
|
+
for (const name of [STATE_FILE, LEGACY_STATE_FILE]) {
|
|
9654
|
+
const path = join(dir, name);
|
|
9655
|
+
if (existsSync(path)) return parseState(readFileSync(path, "utf8"), name);
|
|
9656
|
+
}
|
|
9657
|
+
return null;
|
|
9278
9658
|
}
|
|
9279
9659
|
function writeState(dir, state) {
|
|
9280
9660
|
const out = {
|
|
@@ -9285,10 +9665,15 @@ function writeState(dir, state) {
|
|
|
9285
9665
|
if (state.apiOrigin && state.apiOrigin !== DEFAULT_API_ORIGIN) out.apiOrigin = state.apiOrigin;
|
|
9286
9666
|
mkdirSync(join(dir, STATE_DIR), { recursive: true });
|
|
9287
9667
|
writeFileSync(join(dir, STATE_FILE), `${JSON.stringify(out, null, 2)}\n`);
|
|
9668
|
+
rmSync(join(dir, LEGACY_STATE_FILE), { force: true });
|
|
9288
9669
|
}
|
|
9289
|
-
/** 받은 폴더인가 —
|
|
9670
|
+
/** 연결된(받은) 폴더인가 — 연결 파일 또는 옛 `state.json`·`.sayrenrc`가 있다 */
|
|
9290
9671
|
function isPulledDir(dir) {
|
|
9291
|
-
return
|
|
9672
|
+
return [
|
|
9673
|
+
STATE_FILE,
|
|
9674
|
+
LEGACY_STATE_FILE,
|
|
9675
|
+
LEGACY_RC_FILE
|
|
9676
|
+
].some((name) => existsSync(join(dir, name)));
|
|
9292
9677
|
}
|
|
9293
9678
|
function readMcpText(dir) {
|
|
9294
9679
|
const path = join(dir, MCP_FILE);
|
|
@@ -9327,12 +9712,18 @@ function writePrivate(path, text) {
|
|
|
9327
9712
|
}
|
|
9328
9713
|
}
|
|
9329
9714
|
/**
|
|
9330
|
-
*
|
|
9715
|
+
* 옛 CLI가 받은 폴더를 한 번 정리한다. `.sayren/state.json`(0.2.0 ~ 0.14.x)·`.sayrenrc`(0.1.x) → `.sayren/project.json`(옛 파일은 지운다), `.env`의 폐기된 `SAYREN_TOKEN`(`sy_pat_…`)은
|
|
9331
9716
|
* 지운다(R1 뒤 쓸 수 없다). 다른 값이 든 `SAYREN_TOKEN` 줄은 두고(CLI는 읽지 않는다) 알린다. 정리했으면 한 줄로 알린다.
|
|
9332
9717
|
*/
|
|
9333
9718
|
function migrateLegacy(dir, notify) {
|
|
9334
9719
|
if (!isPulledDir(dir)) return;
|
|
9335
9720
|
const done = [];
|
|
9721
|
+
const legacyState = join(dir, LEGACY_STATE_FILE);
|
|
9722
|
+
if (existsSync(legacyState)) {
|
|
9723
|
+
if (existsSync(join(dir, STATE_FILE))) rmSync(legacyState, { force: true });
|
|
9724
|
+
else writeState(dir, parseState(readFileSync(legacyState, "utf8"), LEGACY_STATE_FILE));
|
|
9725
|
+
done.push(`${LEGACY_STATE_FILE} → ${STATE_FILE}`);
|
|
9726
|
+
}
|
|
9336
9727
|
const rcPath = join(dir, LEGACY_RC_FILE);
|
|
9337
9728
|
if (existsSync(rcPath)) {
|
|
9338
9729
|
const tracked = isGitTracked(dir, LEGACY_RC_FILE);
|
|
@@ -9953,7 +10344,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9953
10344
|
|
|
9954
10345
|
//#endregion
|
|
9955
10346
|
//#region package.json
|
|
9956
|
-
var version = "0.
|
|
10347
|
+
var version = "0.15.0";
|
|
9957
10348
|
|
|
9958
10349
|
//#endregion
|
|
9959
10350
|
//#region src/client.ts
|
|
@@ -11626,42 +12017,49 @@ const FORBIDDEN_SEGMENTS = new Set([
|
|
|
11626
12017
|
".nitro",
|
|
11627
12018
|
".vinxi"
|
|
11628
12019
|
]);
|
|
11629
|
-
/**
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
12020
|
+
/**
|
|
12021
|
+
* 작업 트리에 두지 않는 이름(어디에 있든) — 형식은 맞지만 추적하지 않는다(목록·스냅샷·업로드·쓰기에서 빠진다, #298 P0 거절 목록).
|
|
12022
|
+
* - `.dev.vars`·`.dev.vars.*`(이름 규칙은 `isDevVars`): wrangler 로컬 개발 비밀 값 파일. `.env*`와 같이 업로드·스냅샷·Git push에 들어가지 않게
|
|
12023
|
+
* - `.sayren`·`.sayrenrc`: CLI 연결·상태 파일(`.sayren/project.json`, 옛 `.sayrenrc`는 토큰이 든다). 받기(pull)가 로컬 연결을 덮지 않게
|
|
12024
|
+
* - `.npmrc`·`.yarnrc`·`.yarnrc.yml`·`bunfig.toml`: 레지스트리 인증 토큰이 들어가는 설치 설정. 레지스트리·스크립트 설정이 의존성 판정을 비켜 가지 않게
|
|
12025
|
+
* - `.pnpmfile.cjs`·`.pnpmfile.mjs`: 설치 중 실행되는 훅(`--ignore-scripts`와 무관하게 돈다). 판정한 설치 트리를 바꾸지 않게
|
|
12026
|
+
* - `coverage`·`.turbo`·`.cache`: 테스트·도구 산출물. `.DS_Store`·`Thumbs.db`: OS 부산물
|
|
12027
|
+
*/
|
|
12028
|
+
const UNTRACKED_SEGMENTS = new Set([
|
|
12029
|
+
".sayren",
|
|
12030
|
+
".sayrenrc",
|
|
12031
|
+
".npmrc",
|
|
12032
|
+
".yarnrc",
|
|
12033
|
+
".yarnrc.yml",
|
|
12034
|
+
"bunfig.toml",
|
|
12035
|
+
".pnpmfile.cjs",
|
|
12036
|
+
".pnpmfile.mjs",
|
|
12037
|
+
"coverage",
|
|
12038
|
+
".turbo",
|
|
12039
|
+
".cache",
|
|
12040
|
+
".DS_Store",
|
|
12041
|
+
"Thumbs.db"
|
|
11637
12042
|
]);
|
|
12043
|
+
/** wrangler 로컬 비밀 값 파일(`.dev.vars`, 환경별 `.dev.vars.<env>`) — 어디에 있든 추적하지 않는다 */
|
|
12044
|
+
const isDevVars = (segment) => segment === ".dev.vars" || segment.startsWith(".dev.vars.");
|
|
12045
|
+
/** 루트에 두지 않는 이름 — `pnpm-workspace.yaml`은 사이트를 워크스페이스로 바꾸고 설치 설정(overrides 등)을 싣는다 */
|
|
12046
|
+
const UNTRACKED_ROOT_FILES = new Set(["pnpm-workspace.yaml"]);
|
|
11638
12047
|
/**
|
|
11639
|
-
* 읽기만 되는 루트 파일 —
|
|
11640
|
-
* `
|
|
12048
|
+
* 읽기만 되는 루트 파일 — `CLAUDE.md`·`.mcp.json`은 시작 코드가 싣는 에이전트 안내·MCP 연결 설정이다. 편집 도구는 바꾸지 않는다.
|
|
12049
|
+
* `package.json`·잠금 파일은 쓸 수 있다(#298 P0) — 바뀌면 api가 의존성을 다시 검사한다
|
|
11641
12050
|
*/
|
|
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 상점 도우미가 같은 기준을 읽는다 */
|
|
12051
|
+
const READONLY_ROOT_FILES = new Set(["CLAUDE.md", ".mcp.json"]);
|
|
12052
|
+
/** 읽기만 되는 폴더 — 시작 코드가 싣는 Claude 스킬(`.claude/skills/…`, 2026-10-14). 셀러 로컬 Claude Code와 AI가 같은 기준을 읽는다 */
|
|
11649
12053
|
const READONLY_DIRS = new Set([".claude"]);
|
|
11650
12054
|
/**
|
|
11651
|
-
*
|
|
11652
|
-
*
|
|
12055
|
+
* 스냅샷 묶기에서 빼는 이름(`container-tree.ts`의 `--exclude`) — 금지 조각과 추적하지 않는 이름. 최종 거름은 `isTracked`다
|
|
12056
|
+
* (여기는 큰 설치물·산출물을 tar에 넣지 않으려는 것이다)
|
|
11653
12057
|
*/
|
|
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
|
|
12058
|
+
const SNAPSHOT_EXCLUDED_NAMES = [
|
|
12059
|
+
...FORBIDDEN_SEGMENTS,
|
|
12060
|
+
...UNTRACKED_SEGMENTS,
|
|
12061
|
+
".dev.vars",
|
|
12062
|
+
".dev.vars.*"
|
|
11665
12063
|
];
|
|
11666
12064
|
/** 형식·금지 세그먼트만 본다(위치 규칙은 보지 않는다). 통과하면 null, 아니면 이유 */
|
|
11667
12065
|
function formatProblem(path) {
|
|
@@ -11678,21 +12076,24 @@ function formatProblem(path) {
|
|
|
11678
12076
|
}
|
|
11679
12077
|
return null;
|
|
11680
12078
|
}
|
|
11681
|
-
/**
|
|
12079
|
+
/** 추적하지 않는 이름이면 이유, 아니면 null(형식은 이미 통과한 경로) */
|
|
12080
|
+
function untrackedReason(segments) {
|
|
12081
|
+
if (segments.some((s) => UNTRACKED_SEGMENTS.has(s) || isDevVars(s))) return "untracked";
|
|
12082
|
+
if (segments.length === 1 && UNTRACKED_ROOT_FILES.has(segments[0])) return "untracked";
|
|
12083
|
+
return null;
|
|
12084
|
+
}
|
|
12085
|
+
/**
|
|
12086
|
+
* 위치 규칙: 어떤 모드로 이 경로에 닿을 수 있는가. 거절 목록 방식이다(#298 P0) — 형식(금지 조각·`.env*`·문자·길이)과 추적하지 않는 이름만
|
|
12087
|
+
* 막고 나머지는 어디든 쓸 수 있다. 예외는 읽기만 되는 에이전트 안내(`CLAUDE.md`·`.mcp.json`·`.claude/**`)다
|
|
12088
|
+
*/
|
|
11682
12089
|
function accessOf(path) {
|
|
11683
12090
|
if (formatProblem(path) !== null) return null;
|
|
11684
12091
|
const segments = path.split("/");
|
|
12092
|
+
if (untrackedReason(segments) !== null) return null;
|
|
11685
12093
|
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";
|
|
12094
|
+
if (segments.length === 1 && READONLY_ROOT_FILES.has(first)) return "read";
|
|
12095
|
+
if (segments.length > 1 && READONLY_DIRS.has(first)) return "read";
|
|
12096
|
+
return "write";
|
|
11696
12097
|
}
|
|
11697
12098
|
|
|
11698
12099
|
//#endregion
|
|
@@ -11806,18 +12207,19 @@ async function gunzip(bytes, maxBytes) {
|
|
|
11806
12207
|
//#endregion
|
|
11807
12208
|
//#region src/tree.ts
|
|
11808
12209
|
/**
|
|
11809
|
-
* 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다.
|
|
11810
|
-
*
|
|
12210
|
+
* 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다. 거절 목록 방식이다(#298 P0): 폴더 구조와
|
|
12211
|
+
* 무관하게 올리고, `.env*`·`.mcp.json`·`.sayren/`·`.npmrc`·`node_modules`·`dist` 등 설치물·산출물·비밀·로컬 상태만 올리지 않는다.
|
|
11811
12212
|
* 비밀·로컬 상태 파일(`isNeverUpload`)은 서버 경로 규칙과 별개로 CLI가 먼저 막는다 — 서버 규칙이 넓어져도 토큰을 올리지 않게.
|
|
11812
12213
|
*/
|
|
11813
12214
|
/**
|
|
11814
|
-
* 어떤 경우에도 올리지 않는 파일 — `.env*`(개발 서버 값), `.mcp.json`(토큰), `.sayren/`(CLI 상태),
|
|
12215
|
+
* 어떤 경우에도 올리지 않는 파일 — `.env*`(개발 서버 값), `.dev.vars*`(wrangler 로컬 비밀 값), `.mcp.json`(토큰), `.sayren/`(CLI 상태),
|
|
12216
|
+
* 옛 `.sayrenrc`, `.npmrc`(레지스트리 토큰).
|
|
11815
12217
|
* 폴더 깊이와 무관하게 이름으로 본다.
|
|
11816
12218
|
*/
|
|
11817
12219
|
function isNeverUpload(path) {
|
|
11818
12220
|
const segments = path.split("/");
|
|
11819
12221
|
const name = segments[segments.length - 1] ?? "";
|
|
11820
|
-
return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren") || name === ".mcp.json" || name === ".sayrenrc";
|
|
12222
|
+
return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren" || segment === ".dev.vars" || segment.startsWith(".dev.vars.")) || name === ".mcp.json" || name === ".sayrenrc" || name === ".npmrc";
|
|
11821
12223
|
}
|
|
11822
12224
|
/** 걸어 들어가지 않는 폴더 — 설치물·산출물·캐시·VCS(경로 규칙이 어차피 막는다. 크기 때문에 읽지도 않는다) */
|
|
11823
12225
|
const SKIP_DIRS$1 = new Set([
|
|
@@ -12380,6 +12782,107 @@ async function pickTemplate(templates, wanted, io) {
|
|
|
12380
12782
|
if (index < 0) throw new CliError(cli_init_template_required({ list: labels.map((label) => ` ${label}`).join("\n") }));
|
|
12381
12783
|
return templates[index];
|
|
12382
12784
|
}
|
|
12785
|
+
/**
|
|
12786
|
+
* `sayren link [폴더]`(#315 C6) — 폴더를 상점의 호스팅 사이트에 연결한다. 상점은 `--store` → 토큰 범위 → 대화형 선택이고, 사이트가 없으면
|
|
12787
|
+
* 시작 코드 없이 만든다(`initialSource: "NONE"` — 오픈 준비 중·버전 없음, 첫 deploy가 첫 커밋). 연결 정보는 `.sayren/project.json`, 상점 값은
|
|
12788
|
+
* `env pull`과 같이 `.env`에 쓴다. 토큰은 어느 파일에도 쓰지 않는다.
|
|
12789
|
+
*/
|
|
12790
|
+
async function link(flags, io) {
|
|
12791
|
+
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12792
|
+
if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
|
|
12793
|
+
migrateLegacy(dir, io.out);
|
|
12794
|
+
await linkDir(flags, dir, io);
|
|
12795
|
+
io.out(cli_link_next_steps());
|
|
12796
|
+
return 0;
|
|
12797
|
+
}
|
|
12798
|
+
/** link 본체 — `deploy`도 연결이 없으면 이것부터 부른다 */
|
|
12799
|
+
async function linkDir(flags, dir, io) {
|
|
12800
|
+
let previous = null;
|
|
12801
|
+
try {
|
|
12802
|
+
previous = readState(dir);
|
|
12803
|
+
} catch {
|
|
12804
|
+
previous = null;
|
|
12805
|
+
}
|
|
12806
|
+
const { auth, client } = await apiFor(flags, dir, previous, io);
|
|
12807
|
+
const storeId = auth.storeId;
|
|
12808
|
+
const hosting = await client.storefront.get();
|
|
12809
|
+
if (!hosting.editingEnabled) throw new CliError(cli_link_editing_disabled());
|
|
12810
|
+
let site = hosting.site;
|
|
12811
|
+
if (site) {
|
|
12812
|
+
const git$1 = await client.storefront.getGit().catch(() => null);
|
|
12813
|
+
if (git$1?.owner === "SELLER" && git$1.repository) throw new CliError(cli_deploy_seller_git({
|
|
12814
|
+
repository: git$1.repository,
|
|
12815
|
+
branch: git$1.productionBranch ?? "main"
|
|
12816
|
+
}));
|
|
12817
|
+
} else try {
|
|
12818
|
+
site = await client.storefront.create({ initialSource: "NONE" });
|
|
12819
|
+
io.out(cli_link_site_created({ url: site.url }));
|
|
12820
|
+
} catch (error) {
|
|
12821
|
+
const code = codeOf(error);
|
|
12822
|
+
if (code === "STOREFRONT_SLUG_TAKEN") throw new CliError(cli_link_slug_taken());
|
|
12823
|
+
if (code !== "STOREFRONT_SITE_EXISTS") throw error;
|
|
12824
|
+
site = (await client.storefront.get()).site;
|
|
12825
|
+
if (!site) throw error;
|
|
12826
|
+
}
|
|
12827
|
+
const same = previous?.storeId === storeId && previous.siteId === site.siteId;
|
|
12828
|
+
const state = {
|
|
12829
|
+
storeId,
|
|
12830
|
+
siteId: site.siteId,
|
|
12831
|
+
baseRef: same ? previous?.baseRef ?? null : null,
|
|
12832
|
+
...auth.apiOrigin !== DEFAULT_API_ORIGIN ? { apiOrigin: auth.apiOrigin } : {}
|
|
12833
|
+
};
|
|
12834
|
+
writeState(dir, state);
|
|
12835
|
+
const storeCode = await storeCodeOf(client, storeId);
|
|
12836
|
+
writeStoreEnv(dir, auth.apiOrigin, storeCode);
|
|
12837
|
+
const addedIgnore = ensureGitignore(dir);
|
|
12838
|
+
if (writeMcpJson(dir, { storeId }) === "retired-token-removed") io.out(cli_notice({ message: retiredMcpTokenNotice() }));
|
|
12839
|
+
io.out(cli_link_done({
|
|
12840
|
+
storeId,
|
|
12841
|
+
url: site.url,
|
|
12842
|
+
file: STATE_FILE,
|
|
12843
|
+
envFile: ENV_FILE,
|
|
12844
|
+
storeCode
|
|
12845
|
+
}));
|
|
12846
|
+
if (addedIgnore.length) io.out(cli_link_gitignore({ lines: addedIgnore.join(" ") }));
|
|
12847
|
+
return {
|
|
12848
|
+
client,
|
|
12849
|
+
state
|
|
12850
|
+
};
|
|
12851
|
+
}
|
|
12852
|
+
/** 상점 코드 — 계정의 상점 목록(`stores:read`), 안 되면 작업 트리 조회(`storefront:r`) */
|
|
12853
|
+
async function storeCodeOf(client, storeId) {
|
|
12854
|
+
const listed = await client.auth.listMyStores().then((stores) => stores.find((store) => store.storeId === storeId)?.storeCode).catch(() => void 0);
|
|
12855
|
+
if (listed) return listed;
|
|
12856
|
+
const fromSource = await client.storefront.getSource().then((source) => source.storeCode).catch(() => void 0);
|
|
12857
|
+
if (fromSource) return fromSource;
|
|
12858
|
+
throw new CliError(cli_env_store_code_unknown());
|
|
12859
|
+
}
|
|
12860
|
+
/** `.env`의 상점 연결 값 — 다른 줄은 둔다. 토큰 줄(`SAYREN_TOKEN`)은 지운다 */
|
|
12861
|
+
function writeStoreEnv(dir, apiOrigin, storeCode) {
|
|
12862
|
+
updateEnvFile(dir, {
|
|
12863
|
+
SAYREN_API_URL: `${apiOrigin}/storefront/v1`,
|
|
12864
|
+
SAYREN_STORE_CODE: storeCode
|
|
12865
|
+
}, ["SAYREN_TOKEN"]);
|
|
12866
|
+
}
|
|
12867
|
+
/** `sayren env pull` — 연결된 상점의 `SAYREN_STORE_CODE`·`SAYREN_API_URL`을 `.env`에 쓴다 */
|
|
12868
|
+
async function envPull(flags, io) {
|
|
12869
|
+
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12870
|
+
migrateLegacy(dir, io.out);
|
|
12871
|
+
const state = readState(dir);
|
|
12872
|
+
if (!state) throw new CliError(cli_env_not_linked({ file: STATE_FILE }));
|
|
12873
|
+
const { auth, client } = await apiFor({
|
|
12874
|
+
...flags,
|
|
12875
|
+
store: state.storeId
|
|
12876
|
+
}, dir, state, io);
|
|
12877
|
+
const storeCode = await storeCodeOf(client, state.storeId);
|
|
12878
|
+
writeStoreEnv(dir, auth.apiOrigin, storeCode);
|
|
12879
|
+
ensureGitignore(dir);
|
|
12880
|
+
io.out(cli_env_pulled({
|
|
12881
|
+
file: ENV_FILE,
|
|
12882
|
+
storeCode
|
|
12883
|
+
}));
|
|
12884
|
+
return 0;
|
|
12885
|
+
}
|
|
12383
12886
|
async function verify(flags, io) {
|
|
12384
12887
|
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12385
12888
|
const tree = collectTree(dir);
|
|
@@ -12414,38 +12917,30 @@ function printChecks(tree, checks, io) {
|
|
|
12414
12917
|
}) : cli_skipped_outside({ paths })}`);
|
|
12415
12918
|
}
|
|
12416
12919
|
}
|
|
12920
|
+
/**
|
|
12921
|
+
* `deploy` — 연결 정보(`.sayren/project.json`)가 없으면 `link`부터 한다(#315 C6, Vercel 방식). 막 연결한 폴더는 기준(`baseRef`)이 없어
|
|
12922
|
+
* 확인 없이 다음 버전으로 교체한다(사이트에 소스가 없으면 첫 커밋). 그 뒤 deploy는 올린 트리를 기준으로 조건부 교체한다.
|
|
12923
|
+
*/
|
|
12417
12924
|
async function deploy(flags, io) {
|
|
12418
12925
|
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12419
12926
|
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);
|
|
12927
|
+
const prior = readState(dir);
|
|
12928
|
+
const linked = prior ? await apiFor(flags, dir, prior, io) : null;
|
|
12929
|
+
let tree = collectTree(dir);
|
|
12445
12930
|
const checks = runLocalChecks(tree);
|
|
12446
12931
|
printChecks(tree, checks, io);
|
|
12447
12932
|
if (checks.blockingRules.length > 0 || checks.problems.length > 0) throw new CliError(cli_deploy_checks_failed());
|
|
12448
12933
|
await localBuild(dir, flags.skipBuild === true, io);
|
|
12934
|
+
let client;
|
|
12935
|
+
let rc;
|
|
12936
|
+
if (linked && prior) {
|
|
12937
|
+
client = linked.client;
|
|
12938
|
+
rc = prior;
|
|
12939
|
+
} else {
|
|
12940
|
+
io.out(cli_deploy_linking({ dir: relativeOf(io, dir) }));
|
|
12941
|
+
({client, state: rc} = await linkDir(flags, dir, io));
|
|
12942
|
+
tree = collectTree(dir);
|
|
12943
|
+
}
|
|
12449
12944
|
const site = (await client.storefront.get()).site;
|
|
12450
12945
|
if (!site || site.siteId !== rc.siteId) throw new CliError(cli_deploy_site_mismatch({ siteId: rc.siteId }));
|
|
12451
12946
|
if (flags.force && !flags.yes) {
|
|
@@ -12499,8 +12994,21 @@ async function deploy(flags, io) {
|
|
|
12499
12994
|
io.out(cli_deploy_not_published({ version: created.version.number }));
|
|
12500
12995
|
return 0;
|
|
12501
12996
|
}
|
|
12997
|
+
let current = site.publishedVersion;
|
|
12998
|
+
if (!current) {
|
|
12999
|
+
const after = (await client.storefront.get()).site;
|
|
13000
|
+
if (after?.publishedVersion?.versionId === created.version.versionId) {
|
|
13001
|
+
io.out(cli_deploy_published({
|
|
13002
|
+
version: created.version.number,
|
|
13003
|
+
url: after.url
|
|
13004
|
+
}));
|
|
13005
|
+
io.out(cli_deploy_publish_delay());
|
|
13006
|
+
return 0;
|
|
13007
|
+
}
|
|
13008
|
+
current = after?.publishedVersion ?? null;
|
|
13009
|
+
}
|
|
12502
13010
|
try {
|
|
12503
|
-
const published = await client.storefront.publish(created.version.versionId, { expectedPublishedVersionId:
|
|
13011
|
+
const published = await client.storefront.publish(created.version.versionId, { expectedPublishedVersionId: current?.versionId ?? null });
|
|
12504
13012
|
io.out(cli_deploy_published({
|
|
12505
13013
|
version: created.version.number,
|
|
12506
13014
|
url: published.url
|
|
@@ -12567,7 +13075,7 @@ async function replaceSource(client, tree, rc, force, io) {
|
|
|
12567
13075
|
const packageJson = textOf("package.json");
|
|
12568
13076
|
const lockfile = textOf("pnpm-lock.yaml");
|
|
12569
13077
|
const body = {
|
|
12570
|
-
...force ? { endEditSession: true } : { expectedBaseRef: rc.baseRef },
|
|
13078
|
+
...force ? { endEditSession: true } : rc.baseRef ? { expectedBaseRef: rc.baseRef } : {},
|
|
12571
13079
|
files,
|
|
12572
13080
|
...packageJson === void 0 ? {} : { packageJson },
|
|
12573
13081
|
...lockfile === void 0 ? {} : { lockfile }
|
|
@@ -12739,6 +13247,7 @@ async function status(flags, io) {
|
|
|
12739
13247
|
io.out(` ${editing.session ? cli_status_editing_open({ status: editing.session.status }) : cli_status_editing_closed()}`);
|
|
12740
13248
|
}
|
|
12741
13249
|
if (rc) if (rc.siteId !== site.siteId) io.out(` ${cli_status_folder_other_site({ siteId: rc.siteId })}`);
|
|
13250
|
+
else if (!rc.baseRef) io.out(` ${cli_status_folder_linked_only()}`);
|
|
12742
13251
|
else io.out(` ${cli_status_folder_base({ ref: rc.baseRef.slice(rc.baseRef.lastIndexOf("/") + 1, -4).slice(0, 12) })}`);
|
|
12743
13252
|
return 0;
|
|
12744
13253
|
}
|
|
@@ -12871,6 +13380,57 @@ function relativeOf(io, path) {
|
|
|
12871
13380
|
return relative(io.cwd, path) || ".";
|
|
12872
13381
|
}
|
|
12873
13382
|
|
|
13383
|
+
//#endregion
|
|
13384
|
+
//#region ../mcp/src/starter-transform.ts
|
|
13385
|
+
var StarterTransformError = class extends Error {
|
|
13386
|
+
code;
|
|
13387
|
+
constructor(code, message) {
|
|
13388
|
+
super(message);
|
|
13389
|
+
this.name = "StarterTransformError";
|
|
13390
|
+
this.code = code;
|
|
13391
|
+
}
|
|
13392
|
+
};
|
|
13393
|
+
/** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결 정보 */
|
|
13394
|
+
function isStarterLocalOnly(path) {
|
|
13395
|
+
if (path === ".sayren" || path.startsWith(".sayren/")) return true;
|
|
13396
|
+
if (path.includes("/")) return false;
|
|
13397
|
+
return (path === ".env" || path.startsWith(".env.")) && path !== ".env.example";
|
|
13398
|
+
}
|
|
13399
|
+
function transformStarterFiles(files, options) {
|
|
13400
|
+
const out = /* @__PURE__ */ new Map();
|
|
13401
|
+
for (const [path, bytes] of files) {
|
|
13402
|
+
if (isStarterLocalOnly(path)) continue;
|
|
13403
|
+
const slash = path.lastIndexOf("/");
|
|
13404
|
+
const base = path.slice(slash + 1);
|
|
13405
|
+
out.set(base === "_gitignore" ? `${path.slice(0, slash + 1)}.gitignore` : path, bytes);
|
|
13406
|
+
}
|
|
13407
|
+
const raw = out.get("package.json");
|
|
13408
|
+
if (!raw) throw new StarterTransformError("STARTER_PACKAGE_JSON_MISSING", "package.json이 없습니다");
|
|
13409
|
+
const next = withPackageName(raw, options.name, { private: true });
|
|
13410
|
+
if (!next) throw new StarterTransformError("STARTER_PACKAGE_JSON_INVALID", "package.json을 JSON으로 읽을 수 없습니다");
|
|
13411
|
+
out.set("package.json", next);
|
|
13412
|
+
return out;
|
|
13413
|
+
}
|
|
13414
|
+
/**
|
|
13415
|
+
* `package.json`의 `name`을 바꾼다(`fields`가 있으면 그 필드도). 나머지 필드·순서는 그대로다. JSON 객체가 아니면 null이다.
|
|
13416
|
+
* 편집 Sandbox의 시작 코드 사이트(이미 정규화된 R2 원본)는 이것만 부른다 — 서버가 바꾸는 것은 `name` 하나다.
|
|
13417
|
+
*/
|
|
13418
|
+
function withPackageName(bytes, name, fields = {}) {
|
|
13419
|
+
let pkg;
|
|
13420
|
+
try {
|
|
13421
|
+
pkg = JSON.parse(new TextDecoder().decode(bytes));
|
|
13422
|
+
} catch {
|
|
13423
|
+
return null;
|
|
13424
|
+
}
|
|
13425
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return null;
|
|
13426
|
+
const next = {
|
|
13427
|
+
...pkg,
|
|
13428
|
+
name,
|
|
13429
|
+
...fields
|
|
13430
|
+
};
|
|
13431
|
+
return new TextEncoder().encode(`${JSON.stringify(next, null, 2)}\n`);
|
|
13432
|
+
}
|
|
13433
|
+
|
|
12874
13434
|
//#endregion
|
|
12875
13435
|
//#region ../mcp/src/template-package.ts
|
|
12876
13436
|
/** package.json의 기준 템플릿 표식 필드 — 원천은 이 함수다(api `template-marker.ts`가 같은 모양을 읽는다) */
|
|
@@ -12951,11 +13511,13 @@ function readWorkspaceVersions(packagesDir) {
|
|
|
12951
13511
|
//#endregion
|
|
12952
13512
|
//#region src/create-command.ts
|
|
12953
13513
|
/**
|
|
12954
|
-
* `npx sayren create
|
|
12955
|
-
*
|
|
12956
|
-
*
|
|
13514
|
+
* `npx sayren create <이름> [--template <템플릿>]`(#271, #315 C7) — 시작 코드를 새 폴더에 쓴다. 파일만 만들고 상점을 묻지 않는다.
|
|
13515
|
+
* 상점 연결은 `sayren link`(사이트·`.sayren/project.json`)와 `sayren env pull`(`.env`)이 한다. `--store-code`를 주면 그 값으로 `.env`만 쓴다.
|
|
13516
|
+
*
|
|
13517
|
+
* 템플릿: 없으면 CLI에 묶은 시작 코드(네트워크 없음), `github:owner/repo[/경로][#ref]`는 codeload tarball, 그 밖의 `<id>`는 sayren 레지스트리
|
|
13518
|
+
* 자리다(마켓 전까지 늘 「템플릿을 찾을 수 없습니다」). 받은 소스는 변환 함수 하나(`starter-transform.ts`, 서버와 공유)를 지난다.
|
|
12957
13519
|
*/
|
|
12958
|
-
/**
|
|
13520
|
+
/** 묶은 시작 코드 위치 — 게시본은 빌드가 묶은 `dist/boilerplate`, 저장소에서 바로 돌리면 원천 `packages/cli/boilerplate` */
|
|
12959
13521
|
function boilerplateRoot() {
|
|
12960
13522
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
12961
13523
|
const candidates = [join(here, "boilerplate"), join(here, "..", "boilerplate")];
|
|
@@ -12963,18 +13525,7 @@ function boilerplateRoot() {
|
|
|
12963
13525
|
if (!found) throw new Error(`boilerplate not found: ${candidates.join(", ")}`);
|
|
12964
13526
|
return found;
|
|
12965
13527
|
}
|
|
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`만 */
|
|
13528
|
+
/** 싣지 않는 것 — 설치물·산출물. 점 파일은 에이전트 안내(`.claude/`·`.mcp.json`)와 `.gitignore`만(scripts/starter-bundle.mjs와 같다) */
|
|
12978
13529
|
const SKIP_DIRS = new Set([
|
|
12979
13530
|
"node_modules",
|
|
12980
13531
|
"dist",
|
|
@@ -12988,8 +13539,11 @@ const DOT_KEPT = new Set([
|
|
|
12988
13539
|
".mcp.json",
|
|
12989
13540
|
".gitignore"
|
|
12990
13541
|
]);
|
|
12991
|
-
/**
|
|
12992
|
-
|
|
13542
|
+
/**
|
|
13543
|
+
* 묶은 시작 코드 — 상대 경로 → 내용. 게시본은 빌드가 이미 npm 범위로 풀었다. 저장소 원천(워크스페이스 구성원)에서 바로 돌리면
|
|
13544
|
+
* `catalog:`·`workspace:`를 여기서 푼다(게시본과 같은 결과).
|
|
13545
|
+
*/
|
|
13546
|
+
function bundledStarterFiles(root) {
|
|
12993
13547
|
const files = /* @__PURE__ */ new Map();
|
|
12994
13548
|
const walk = (dir) => {
|
|
12995
13549
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
@@ -12997,16 +13551,86 @@ function boilerplateFiles(root, name) {
|
|
|
12997
13551
|
if (entry.name.startsWith(".") && !DOT_KEPT.has(entry.name)) continue;
|
|
12998
13552
|
const full = join(dir, entry.name);
|
|
12999
13553
|
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
|
-
}
|
|
13554
|
+
else if (entry.isFile()) files.set(relative(root, full).split(sep).join("/"), readFileSync(full));
|
|
13004
13555
|
}
|
|
13005
13556
|
};
|
|
13006
13557
|
walk(root);
|
|
13558
|
+
const repo = join(root, "..", "..", "..");
|
|
13007
13559
|
const pkg = files.get("package.json");
|
|
13008
|
-
if (
|
|
13009
|
-
|
|
13560
|
+
if (pkg && existsSync(join(repo, "pnpm-workspace.yaml"))) {
|
|
13561
|
+
const meta = {
|
|
13562
|
+
catalog: parseCatalog(readFileSync(join(repo, "pnpm-workspace.yaml"), "utf8")),
|
|
13563
|
+
versions: readWorkspaceVersions(join(repo, "packages"))
|
|
13564
|
+
};
|
|
13565
|
+
files.set("package.json", Buffer.from(renderTemplatePackageJson(Buffer.from(pkg).toString("utf8"), meta)));
|
|
13566
|
+
}
|
|
13567
|
+
return files;
|
|
13568
|
+
}
|
|
13569
|
+
const GITHUB_OWNER = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
|
|
13570
|
+
const GITHUB_REPO = /^[A-Za-z0-9._-]{1,100}$/;
|
|
13571
|
+
const PATH_SEGMENT = /^[A-Za-z0-9._@+-]{1,255}$/;
|
|
13572
|
+
const GIT_REF = /^[A-Za-z0-9._/+-]{1,255}$/;
|
|
13573
|
+
/** `--template` 값 → 받을 곳. 형식이 틀리면 api·네트워크를 부르기 전에 멈춘다 */
|
|
13574
|
+
function parseTemplateSpec(value) {
|
|
13575
|
+
const raw = value?.trim();
|
|
13576
|
+
if (!raw) return { kind: "bundled" };
|
|
13577
|
+
if (!raw.startsWith("github:")) return {
|
|
13578
|
+
kind: "registry",
|
|
13579
|
+
id: raw
|
|
13580
|
+
};
|
|
13581
|
+
const body = raw.slice(7);
|
|
13582
|
+
const hash = body.indexOf("#");
|
|
13583
|
+
const location = hash < 0 ? body : body.slice(0, hash);
|
|
13584
|
+
const ref = hash < 0 ? null : body.slice(hash + 1);
|
|
13585
|
+
const [owner = "", repo = "", ...rest] = location.split("/");
|
|
13586
|
+
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 }));
|
|
13587
|
+
return {
|
|
13588
|
+
kind: "github",
|
|
13589
|
+
owner,
|
|
13590
|
+
repo,
|
|
13591
|
+
path: rest.join("/"),
|
|
13592
|
+
ref
|
|
13593
|
+
};
|
|
13594
|
+
}
|
|
13595
|
+
/** codeload tarball 주소 — ref가 없으면 기본 브랜치(`HEAD`) */
|
|
13596
|
+
function codeloadUrlOf(spec) {
|
|
13597
|
+
const ref = spec.ref ? spec.ref.split("/").map(encodeURIComponent).join("/") : "HEAD";
|
|
13598
|
+
return `https://codeload.github.com/${spec.owner}/${spec.repo}/tar.gz/${ref}`;
|
|
13599
|
+
}
|
|
13600
|
+
/** 받은 저장소 tarball을 풀어 둘 상한(압축 폭탄 방어선). 템플릿 저장소가 모노레포여도 정상 저장소는 닿지 않는다 */
|
|
13601
|
+
const TEMPLATE_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024;
|
|
13602
|
+
/** GitHub 템플릿 — tarball을 받아 맨 위 폴더(`{repo}-{sha}/`)를 벗기고 `경로` 아래만 남긴다 */
|
|
13603
|
+
async function githubTemplateFiles(spec, label, io) {
|
|
13604
|
+
let res;
|
|
13605
|
+
try {
|
|
13606
|
+
res = await io.fetch(codeloadUrlOf(spec));
|
|
13607
|
+
} catch {
|
|
13608
|
+
throw new CliError(cli_create_template_unreachable({ template: label }));
|
|
13609
|
+
}
|
|
13610
|
+
if (res.status === 404) throw new CliError(cli_create_template_github_not_found({ template: label }));
|
|
13611
|
+
if (!res.ok) throw new CliError(cli_create_template_download_failed({
|
|
13612
|
+
template: label,
|
|
13613
|
+
status: String(res.status)
|
|
13614
|
+
}));
|
|
13615
|
+
let entries;
|
|
13616
|
+
try {
|
|
13617
|
+
entries = readTar(await gunzip(new Uint8Array(await res.arrayBuffer()), TEMPLATE_ARCHIVE_MAX_BYTES), TEMPLATE_ARCHIVE_MAX_BYTES);
|
|
13618
|
+
} catch (error) {
|
|
13619
|
+
if (error instanceof TarLimitError) throw new CliError(cli_create_template_too_large({ template: label }));
|
|
13620
|
+
throw new CliError(cli_create_template_unreadable({ template: label }));
|
|
13621
|
+
}
|
|
13622
|
+
const prefix = spec.path ? `${spec.path}/` : "";
|
|
13623
|
+
const files = /* @__PURE__ */ new Map();
|
|
13624
|
+
for (const entry of entries) {
|
|
13625
|
+
const slash = entry.path.indexOf("/");
|
|
13626
|
+
if (slash < 0) continue;
|
|
13627
|
+
const inRepo = entry.path.slice(slash + 1);
|
|
13628
|
+
if (!inRepo.startsWith(prefix)) continue;
|
|
13629
|
+
const path = inRepo.slice(prefix.length);
|
|
13630
|
+
if (!path || path.startsWith("/") || path.split("/").some((s) => s === "" || s === "..")) continue;
|
|
13631
|
+
files.set(path, entry.bytes);
|
|
13632
|
+
}
|
|
13633
|
+
if (files.size === 0) throw new CliError(cli_create_template_path_empty({ template: label }));
|
|
13010
13634
|
return files;
|
|
13011
13635
|
}
|
|
13012
13636
|
/** npm 패키지 이름으로도 쓰는 폴더 이름 — 범위(@scope/) 없는 이름만 */
|
|
@@ -13017,24 +13641,33 @@ async function create(flags, io) {
|
|
|
13017
13641
|
if (!NAME_PATTERN.test(name)) throw new CliError(cli_create_name_invalid({ name }));
|
|
13018
13642
|
const target = resolve(io.cwd, name);
|
|
13019
13643
|
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
|
-
|
|
13644
|
+
const storeCode = flags.storeCode?.trim();
|
|
13645
|
+
if (storeCode !== void 0 && !STORE_CODE_PATTERN.test(storeCode)) throw new CliError(cli_create_store_code_invalid({ storeCode }));
|
|
13646
|
+
const spec = parseTemplateSpec(flags.template);
|
|
13647
|
+
if (spec.kind === "registry") throw new CliError(cli_create_template_not_found({ template: spec.id }));
|
|
13648
|
+
const source = spec.kind === "github" ? await githubTemplateFiles(spec, flags.template?.trim() ?? "", io) : bundledStarterFiles(boilerplateRoot());
|
|
13649
|
+
let files;
|
|
13650
|
+
try {
|
|
13651
|
+
files = transformStarterFiles(source, { name });
|
|
13652
|
+
} catch (error) {
|
|
13653
|
+
if (error instanceof StarterTransformError) throw new CliError(cli_create_template_no_package_json({ reason: error.message }));
|
|
13654
|
+
throw error;
|
|
13029
13655
|
}
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13656
|
+
writeTree(target, files);
|
|
13657
|
+
if (storeCode) {
|
|
13658
|
+
const { apiOrigin } = resolveApiOrigin(flags, target, null, io.env);
|
|
13659
|
+
updateEnvFile(target, {
|
|
13660
|
+
SAYREN_API_URL: `${apiOrigin}/storefront/v1`,
|
|
13661
|
+
SAYREN_STORE_CODE: storeCode
|
|
13662
|
+
});
|
|
13663
|
+
io.out(cli_create_done_store_code({
|
|
13664
|
+
dir: name,
|
|
13665
|
+
count: files.size,
|
|
13666
|
+
storeCode
|
|
13667
|
+
}));
|
|
13668
|
+
} else io.out(cli_create_done({
|
|
13035
13669
|
dir: name,
|
|
13036
|
-
count: files.size
|
|
13037
|
-
storeCode
|
|
13670
|
+
count: files.size
|
|
13038
13671
|
}));
|
|
13039
13672
|
return 0;
|
|
13040
13673
|
}
|
|
@@ -13183,8 +13816,16 @@ async function main(argv, io) {
|
|
|
13183
13816
|
case "create": return create({
|
|
13184
13817
|
name: parsed.positional[0],
|
|
13185
13818
|
storeCode: str(parsed.flags["store-code"]),
|
|
13186
|
-
apiOrigin: str(parsed.flags["api-origin"])
|
|
13819
|
+
apiOrigin: str(parsed.flags["api-origin"]),
|
|
13820
|
+
template: str(parsed.flags.template)
|
|
13187
13821
|
}, io);
|
|
13822
|
+
case "link": return link(common, io);
|
|
13823
|
+
case "env":
|
|
13824
|
+
if (parsed.positional[0] !== "pull") throw new CliError(cli_env_usage());
|
|
13825
|
+
return envPull({
|
|
13826
|
+
...common,
|
|
13827
|
+
dir: str(parsed.flags.dir) ?? parsed.positional[1]
|
|
13828
|
+
}, io);
|
|
13188
13829
|
case "init": return init({
|
|
13189
13830
|
...common,
|
|
13190
13831
|
template: str(parsed.flags.template),
|