sayren 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +187 -16
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -890,7 +890,8 @@ const cli_app_dev_error = ((inputs, options = {}) => {
|
|
|
890
890
|
const ko_cli_app_dev_listening = (i) => {
|
|
891
891
|
return `앱 로컬 실행 중 http://localhost:${i?.port} — 상점 ${i?.store} · 이벤트 ${i?.events}
|
|
892
892
|
이벤트: npx sayren app trigger ORDER.PAID --order <orderId>
|
|
893
|
-
HTTP: http://localhost:${i?.port}/_http/<경로>
|
|
893
|
+
HTTP: http://localhost:${i?.port}/_http/<경로> · 공개 HTTP: http://localhost:${i?.port}/hooks/<설치 id>/<경로>
|
|
894
|
+
예약 작업: npx sayren app trigger --schedule <이름>
|
|
894
895
|
설정·비밀 값: .env의 SAYREN_APP_SETTING_*·SAYREN_APP_SECRET_*
|
|
895
896
|
종료는 Ctrl-C`;
|
|
896
897
|
};
|
|
@@ -1036,7 +1037,7 @@ const cli_app_handle_mismatch = ((inputs, options = {}) => {
|
|
|
1036
1037
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1037
1038
|
/** @typedef {{ events: NonNullable<unknown> }} Cli_App_Handler_MissingInputs */
|
|
1038
1039
|
const ko_cli_app_handler_missing = (i) => {
|
|
1039
|
-
return `매니페스트
|
|
1040
|
+
return `매니페스트(events·schedules·http.public·flow.notifications·mcp.tools)에 있지만 코드에 함수가 없습니다: ${i?.events} (HANDLER_MISSING)`;
|
|
1040
1041
|
};
|
|
1041
1042
|
/**
|
|
1042
1043
|
|
|
@@ -1532,7 +1533,7 @@ const cli_app_trigger_failed = ((inputs, options = {}) => {
|
|
|
1532
1533
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1533
1534
|
/** @typedef {{}} Cli_App_Trigger_Type_RequiredInputs */
|
|
1534
1535
|
const ko_cli_app_trigger_type_required = () => {
|
|
1535
|
-
return `이벤트 종류가 필요합니다. 예: npx sayren app trigger ORDER.PAID --order ord_
|
|
1536
|
+
return `이벤트 종류가 필요합니다. 예: npx sayren app trigger ORDER.PAID --order ord_… (예약 작업은 --schedule <이름>)`;
|
|
1536
1537
|
};
|
|
1537
1538
|
/**
|
|
1538
1539
|
|
|
@@ -5249,11 +5250,15 @@ const ko_cli_usage = (i) => {
|
|
|
5249
5250
|
--key <키> app: 앱 배포 키(developer.sayren.app에서 발행, SAYREN_APP_KEY). CI에서 deploy·release·status·logs·secret에 씁니다
|
|
5250
5251
|
-m, --message <메모> app deploy: 버전 메모
|
|
5251
5252
|
--name <이름> app init: 앱 이름(기본: handle)
|
|
5253
|
+
--private-store <storeId> app init: 이 상점 하나에만 설치되는 비공개 앱으로 만듭니다
|
|
5252
5254
|
--port <포트> app dev·trigger: 로컬 포트(기본 4020)
|
|
5253
5255
|
--since <기간> app logs: 최근 기간만(30m·1h·2d)
|
|
5254
5256
|
--tail app logs: 새 기록을 계속 봅니다
|
|
5255
5257
|
--data <json> app trigger: 이벤트 데이터
|
|
5256
5258
|
--order <orderId> app trigger: 주문 id를 데이터에 넣습니다
|
|
5259
|
+
--schedule <이름> app trigger: 이벤트 대신 예약 작업을 실행합니다
|
|
5260
|
+
--flow <이름> app trigger: 이벤트 대신 흐름 알림을 보냅니다(--data는 알림 값)
|
|
5261
|
+
--tool <이름> app trigger: 이벤트 대신 에이전트 도구를 부릅니다(--data는 입력)
|
|
5257
5262
|
--lang <로케일> 메시지 언어(기본: SAYREN_LANG → 시스템 로케일 → ko)
|
|
5258
5263
|
|
|
5259
5264
|
종료 코드: 0 성공 · 1 실패 · 3 승인 대기(에이전트 토큰)`;
|
|
@@ -9177,7 +9182,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9177
9182
|
|
|
9178
9183
|
//#endregion
|
|
9179
9184
|
//#region package.json
|
|
9180
|
-
var version = "0.
|
|
9185
|
+
var version = "0.8.0";
|
|
9181
9186
|
|
|
9182
9187
|
//#endregion
|
|
9183
9188
|
//#region src/client.ts
|
|
@@ -9391,7 +9396,10 @@ async function loadBundle(dir, server) {
|
|
|
9391
9396
|
app,
|
|
9392
9397
|
meta: {
|
|
9393
9398
|
events: [...meta.events],
|
|
9394
|
-
http: [...meta.http]
|
|
9399
|
+
http: [...meta.http],
|
|
9400
|
+
schedules: [...meta.schedules ?? []],
|
|
9401
|
+
flows: [...meta.flows ?? []],
|
|
9402
|
+
tools: [...meta.tools ?? []]
|
|
9395
9403
|
}
|
|
9396
9404
|
};
|
|
9397
9405
|
}
|
|
@@ -9432,14 +9440,39 @@ function collectListing(dir, manifest) {
|
|
|
9432
9440
|
};
|
|
9433
9441
|
}
|
|
9434
9442
|
/** 로컬 검사 — 매니페스트, 번들, 이벤트 함수 존재, 비밀 값 형식, 크기. 서버 `VERIFY`·`BUNDLE`과 같은 규칙 */
|
|
9443
|
+
/**
|
|
9444
|
+
* 외부 런타임 앱(매뉴얼 「개발자 서버에서 실행」)이 올리는 서버 자리 — 코드는 개발자 서버에 있어 sayren 실행 환경에는 이것만 둔다(호출되지 않는다)
|
|
9445
|
+
*/
|
|
9446
|
+
const EXTERNAL_STUB_SERVER = "export default { fetch() { return new Response(\"external runtime\", { status: 404 }); } };\n";
|
|
9447
|
+
const EMPTY_META = {
|
|
9448
|
+
events: [],
|
|
9449
|
+
http: [],
|
|
9450
|
+
schedules: [],
|
|
9451
|
+
flows: [],
|
|
9452
|
+
tools: []
|
|
9453
|
+
};
|
|
9435
9454
|
async function verifyApp(dir) {
|
|
9436
9455
|
const { raw, manifest } = loadManifest(dir);
|
|
9437
|
-
const
|
|
9456
|
+
const external = manifest.runtime === "external";
|
|
9457
|
+
const server = external ? EXTERNAL_STUB_SERVER : await bundleServer(dir);
|
|
9438
9458
|
if (APP_SECRET_PATTERNS.find((pattern) => pattern.test(server))) throw new CliError(cli_app_secret_in_source());
|
|
9439
9459
|
const gzipBytes = gzipSync(server).byteLength;
|
|
9440
9460
|
if (gzipBytes > APP_SERVER_BUNDLE_MAX_GZIP_BYTES) throw new CliError(cli_app_bundle_too_large({ size: formatMb(gzipBytes) }));
|
|
9441
|
-
const { app, meta } =
|
|
9442
|
-
|
|
9461
|
+
const { app, meta } = external ? {
|
|
9462
|
+
app: {
|
|
9463
|
+
fetch: async () => new Response("external runtime", { status: 404 }),
|
|
9464
|
+
__sayrenApp: EMPTY_META
|
|
9465
|
+
},
|
|
9466
|
+
meta: { ...EMPTY_META }
|
|
9467
|
+
} : await loadBundle(dir, server);
|
|
9468
|
+
const httpKeys = new Set(meta.http.map((key) => key.trim().replace(/\s+/g, " ")));
|
|
9469
|
+
const missing = external ? [] : [
|
|
9470
|
+
...manifest.events.filter((event) => !meta.events.includes(event)),
|
|
9471
|
+
...Object.keys(manifest.schedules).filter((name) => !meta.schedules.includes(name)),
|
|
9472
|
+
...Object.keys(manifest.flow?.notifications ?? {}).filter((name) => !meta.flows.includes(name)),
|
|
9473
|
+
...Object.keys(manifest.mcp?.tools ?? {}).filter((name) => !meta.tools.includes(name)),
|
|
9474
|
+
...(manifest.http?.public ?? []).filter((key) => !httpKeys.has(key))
|
|
9475
|
+
];
|
|
9443
9476
|
if (missing.length) throw new CliError(cli_app_handler_missing({ events: missing.join(", ") }));
|
|
9444
9477
|
const warnings = [];
|
|
9445
9478
|
const listed = manifest.events;
|
|
@@ -9554,9 +9587,11 @@ async function appInit(handle, flags, io) {
|
|
|
9554
9587
|
if (existsSync(join(dir, APP_MANIFEST_FILE))) throw new CliError(cli_app_dir_exists({ dir: relative(io.cwd, dir) || "." }));
|
|
9555
9588
|
const name = flags.name?.trim() || handle;
|
|
9556
9589
|
const auth = await developerAuth(flags, dir, io, { accountOnly: true });
|
|
9590
|
+
const privateStoreId = flags.privateStore?.trim();
|
|
9557
9591
|
const app = await devApi(auth, io, "POST", "/apps", {
|
|
9558
9592
|
handle,
|
|
9559
|
-
name
|
|
9593
|
+
name,
|
|
9594
|
+
...privateStoreId ? { privateStoreId } : {}
|
|
9560
9595
|
});
|
|
9561
9596
|
mkdirSync(join(dir, "src"), { recursive: true });
|
|
9562
9597
|
for (const [path, content] of Object.entries(initFiles(handle, name))) {
|
|
@@ -9803,15 +9838,27 @@ function devEnvelope(input) {
|
|
|
9803
9838
|
versionNumber: 0,
|
|
9804
9839
|
storeId: input.token?.storeId ?? "store_dev",
|
|
9805
9840
|
storeName: "(dev)",
|
|
9806
|
-
apiBaseUrl: input.apiBaseUrl
|
|
9841
|
+
apiBaseUrl: input.apiBaseUrl,
|
|
9842
|
+
publicBaseUrl: `${input.publicOrigin}/hooks/${input.token?.installationId ?? "inst_dev"}`
|
|
9807
9843
|
},
|
|
9808
9844
|
token: input.token?.token ?? null,
|
|
9809
9845
|
settings: input.settings,
|
|
9810
9846
|
secrets: input.secrets,
|
|
9811
|
-
...input.kind === "http" ? { user: {
|
|
9847
|
+
...input.kind === "http" && !input.public ? { user: {
|
|
9812
9848
|
userId: "user_dev",
|
|
9813
9849
|
role: "OWNER"
|
|
9814
9850
|
} } : {},
|
|
9851
|
+
...input.schedule ? { schedule: input.schedule } : {},
|
|
9852
|
+
...input.tool ? { tool: input.tool } : {},
|
|
9853
|
+
...input.flow ? { flow: {
|
|
9854
|
+
orderId: "ord_dev",
|
|
9855
|
+
orderItemIds: ["oi_dev"],
|
|
9856
|
+
flowId: "flow_dev",
|
|
9857
|
+
flowVersion: 1,
|
|
9858
|
+
state: "dev",
|
|
9859
|
+
params: {},
|
|
9860
|
+
...input.flow
|
|
9861
|
+
} } : {},
|
|
9815
9862
|
...input.event ? { event: {
|
|
9816
9863
|
id: `evt_dev_${devSeq}`,
|
|
9817
9864
|
type: input.event.type,
|
|
@@ -9880,12 +9927,71 @@ async function appDev(flags, io) {
|
|
|
9880
9927
|
await ensureToken();
|
|
9881
9928
|
const local = () => localSettingsOf(manifest, readEnvFile(dir));
|
|
9882
9929
|
const apiBaseUrl = `${state.apiOrigin}/v1`;
|
|
9930
|
+
const publicOrigin = `http://localhost:${port}`;
|
|
9883
9931
|
const uiRoot = join(dir, UI_DIR);
|
|
9884
9932
|
const handle = async (req, res) => {
|
|
9885
9933
|
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
9886
9934
|
try {
|
|
9887
9935
|
if (req.method === "POST" && url.pathname === "/_trigger") {
|
|
9888
9936
|
const body = JSON.parse(await readBody(req) || "{}");
|
|
9937
|
+
if (body.tool) {
|
|
9938
|
+
const { settings: settings$1, secrets: secrets$1 } = local();
|
|
9939
|
+
const result$1 = await invokeLocal(app, devEnvelope({
|
|
9940
|
+
kind: "tool",
|
|
9941
|
+
tool: {
|
|
9942
|
+
name: body.tool,
|
|
9943
|
+
input: body.data ?? {}
|
|
9944
|
+
},
|
|
9945
|
+
publicOrigin,
|
|
9946
|
+
token: await ensureToken(),
|
|
9947
|
+
apiBaseUrl,
|
|
9948
|
+
appId: state.appId,
|
|
9949
|
+
settings: settings$1,
|
|
9950
|
+
secrets: secrets$1
|
|
9951
|
+
}));
|
|
9952
|
+
printResult(`[tool] ${body.tool}`, result$1, io);
|
|
9953
|
+
if (result$1.response?.body) io.out(` → ${result$1.response.body}`);
|
|
9954
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
9955
|
+
res.end(JSON.stringify(result$1));
|
|
9956
|
+
return;
|
|
9957
|
+
}
|
|
9958
|
+
if (body.flow) {
|
|
9959
|
+
const { settings: settings$1, secrets: secrets$1 } = local();
|
|
9960
|
+
const result$1 = await invokeLocal(app, devEnvelope({
|
|
9961
|
+
kind: "flow",
|
|
9962
|
+
flow: {
|
|
9963
|
+
...body.data ?? {},
|
|
9964
|
+
name: body.flow
|
|
9965
|
+
},
|
|
9966
|
+
publicOrigin,
|
|
9967
|
+
token: await ensureToken(),
|
|
9968
|
+
apiBaseUrl,
|
|
9969
|
+
appId: state.appId,
|
|
9970
|
+
settings: settings$1,
|
|
9971
|
+
secrets: secrets$1
|
|
9972
|
+
}));
|
|
9973
|
+
printResult(`[flow] ${body.flow}`, result$1, io);
|
|
9974
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
9975
|
+
res.end(JSON.stringify(result$1));
|
|
9976
|
+
return;
|
|
9977
|
+
}
|
|
9978
|
+
if (body.schedule) {
|
|
9979
|
+
const { settings: settings$1, secrets: secrets$1 } = local();
|
|
9980
|
+
const result$1 = await invokeLocal(app, devEnvelope({
|
|
9981
|
+
kind: "schedule",
|
|
9982
|
+
schedule: body.schedule,
|
|
9983
|
+
publicOrigin,
|
|
9984
|
+
token: await ensureToken(),
|
|
9985
|
+
apiBaseUrl,
|
|
9986
|
+
appId: state.appId,
|
|
9987
|
+
settings: settings$1,
|
|
9988
|
+
secrets: secrets$1
|
|
9989
|
+
}));
|
|
9990
|
+
printResult(`[schedule] ${body.schedule}`, result$1, io);
|
|
9991
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
9992
|
+
res.end(JSON.stringify(result$1));
|
|
9993
|
+
return;
|
|
9994
|
+
}
|
|
9889
9995
|
if (!body.type) {
|
|
9890
9996
|
res.writeHead(400, { "content-type": "application/json" });
|
|
9891
9997
|
res.end(JSON.stringify({ error: "TYPE_REQUIRED" }));
|
|
@@ -9894,6 +10000,7 @@ async function appDev(flags, io) {
|
|
|
9894
10000
|
const { settings, secrets } = local();
|
|
9895
10001
|
const result = await invokeLocal(app, devEnvelope({
|
|
9896
10002
|
kind: "event",
|
|
10003
|
+
publicOrigin,
|
|
9897
10004
|
token: await ensureToken(),
|
|
9898
10005
|
apiBaseUrl,
|
|
9899
10006
|
appId: state.appId,
|
|
@@ -9909,13 +10016,17 @@ async function appDev(flags, io) {
|
|
|
9909
10016
|
res.end(JSON.stringify(result));
|
|
9910
10017
|
return;
|
|
9911
10018
|
}
|
|
9912
|
-
|
|
10019
|
+
const hook = /^\/hooks\/[^/]+(\/.*)$/.exec(url.pathname);
|
|
10020
|
+
if (url.pathname.startsWith("/_http/") || hook) {
|
|
10021
|
+
const route = hook ? hook[1] : url.pathname.slice(6);
|
|
9913
10022
|
const headers = {};
|
|
9914
10023
|
for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string" && key !== "host") headers[key] = value;
|
|
9915
10024
|
const method = (req.method ?? "GET").toUpperCase();
|
|
9916
10025
|
const { settings, secrets } = local();
|
|
9917
10026
|
const result = await invokeLocal(app, devEnvelope({
|
|
9918
10027
|
kind: "http",
|
|
10028
|
+
public: Boolean(hook),
|
|
10029
|
+
publicOrigin,
|
|
9919
10030
|
token: await ensureToken(),
|
|
9920
10031
|
apiBaseUrl,
|
|
9921
10032
|
appId: state.appId,
|
|
@@ -9923,12 +10034,12 @@ async function appDev(flags, io) {
|
|
|
9923
10034
|
secrets,
|
|
9924
10035
|
request: {
|
|
9925
10036
|
method,
|
|
9926
|
-
path: `${
|
|
10037
|
+
path: `${route}${url.search}`,
|
|
9927
10038
|
headers,
|
|
9928
10039
|
body: method === "GET" ? null : await readBody(req)
|
|
9929
10040
|
}
|
|
9930
10041
|
}));
|
|
9931
|
-
printResult(`[http] ${method} ${
|
|
10042
|
+
printResult(`[${hook ? "hook" : "http"}] ${method} ${route}`, result, io);
|
|
9932
10043
|
if (!result.response) {
|
|
9933
10044
|
res.writeHead(500, { "content-type": "application/json" });
|
|
9934
10045
|
res.end(JSON.stringify({
|
|
@@ -9982,6 +10093,59 @@ async function appDev(flags, io) {
|
|
|
9982
10093
|
return 0;
|
|
9983
10094
|
}
|
|
9984
10095
|
async function appTrigger(type, flags, io) {
|
|
10096
|
+
const port = Number(flags.port ?? io.env.SAYREN_APP_PORT ?? APP_DEV_PORT);
|
|
10097
|
+
if (flags.tool) {
|
|
10098
|
+
let toolInput = {};
|
|
10099
|
+
if (flags.data) try {
|
|
10100
|
+
toolInput = JSON.parse(flags.data);
|
|
10101
|
+
} catch {
|
|
10102
|
+
throw new CliError(cli_app_trigger_data_invalid());
|
|
10103
|
+
}
|
|
10104
|
+
const res$1 = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
10105
|
+
method: "POST",
|
|
10106
|
+
headers: { "content-type": "application/json" },
|
|
10107
|
+
body: JSON.stringify({
|
|
10108
|
+
tool: flags.tool,
|
|
10109
|
+
data: toolInput
|
|
10110
|
+
})
|
|
10111
|
+
});
|
|
10112
|
+
if (!res$1.ok) throw new CliError(cli_app_trigger_failed({ status: String(res$1.status) }));
|
|
10113
|
+
const result$1 = await res$1.json();
|
|
10114
|
+
printResult(`[tool] ${flags.tool}`, result$1, io);
|
|
10115
|
+
if (result$1.response?.body) io.out(` → ${result$1.response.body}`);
|
|
10116
|
+
return result$1.outcome === "OK" ? 0 : 1;
|
|
10117
|
+
}
|
|
10118
|
+
if (flags.flow) {
|
|
10119
|
+
let flowData = {};
|
|
10120
|
+
if (flags.data) try {
|
|
10121
|
+
flowData = JSON.parse(flags.data);
|
|
10122
|
+
} catch {
|
|
10123
|
+
throw new CliError(cli_app_trigger_data_invalid());
|
|
10124
|
+
}
|
|
10125
|
+
const res$1 = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
10126
|
+
method: "POST",
|
|
10127
|
+
headers: { "content-type": "application/json" },
|
|
10128
|
+
body: JSON.stringify({
|
|
10129
|
+
flow: flags.flow,
|
|
10130
|
+
data: flowData
|
|
10131
|
+
})
|
|
10132
|
+
});
|
|
10133
|
+
if (!res$1.ok) throw new CliError(cli_app_trigger_failed({ status: String(res$1.status) }));
|
|
10134
|
+
const result$1 = await res$1.json();
|
|
10135
|
+
printResult(`[flow] ${flags.flow}`, result$1, io);
|
|
10136
|
+
return result$1.outcome === "OK" ? 0 : 1;
|
|
10137
|
+
}
|
|
10138
|
+
if (flags.schedule) {
|
|
10139
|
+
const res$1 = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
10140
|
+
method: "POST",
|
|
10141
|
+
headers: { "content-type": "application/json" },
|
|
10142
|
+
body: JSON.stringify({ schedule: flags.schedule })
|
|
10143
|
+
});
|
|
10144
|
+
if (!res$1.ok) throw new CliError(cli_app_trigger_failed({ status: String(res$1.status) }));
|
|
10145
|
+
const result$1 = await res$1.json();
|
|
10146
|
+
printResult(`[schedule] ${flags.schedule}`, result$1, io);
|
|
10147
|
+
return result$1.outcome === "OK" ? 0 : 1;
|
|
10148
|
+
}
|
|
9985
10149
|
if (!type) throw new CliError(cli_app_trigger_type_required());
|
|
9986
10150
|
let data = {};
|
|
9987
10151
|
if (flags.data) try {
|
|
@@ -9993,7 +10157,6 @@ async function appTrigger(type, flags, io) {
|
|
|
9993
10157
|
...data,
|
|
9994
10158
|
orderId: flags.order
|
|
9995
10159
|
};
|
|
9996
|
-
const port = Number(flags.port ?? io.env.SAYREN_APP_PORT ?? APP_DEV_PORT);
|
|
9997
10160
|
const res = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
9998
10161
|
method: "POST",
|
|
9999
10162
|
headers: { "content-type": "application/json" },
|
|
@@ -11905,7 +12068,11 @@ const VALUE_FLAGS = new Set([
|
|
|
11905
12068
|
"port",
|
|
11906
12069
|
"since",
|
|
11907
12070
|
"data",
|
|
11908
|
-
"order"
|
|
12071
|
+
"order",
|
|
12072
|
+
"schedule",
|
|
12073
|
+
"private-store",
|
|
12074
|
+
"flow",
|
|
12075
|
+
"tool"
|
|
11909
12076
|
]);
|
|
11910
12077
|
const BOOL_FLAGS = new Set([
|
|
11911
12078
|
"force",
|
|
@@ -11990,6 +12157,10 @@ async function main(argv, io) {
|
|
|
11990
12157
|
tail: parsed.flags.tail === true,
|
|
11991
12158
|
data: str(parsed.flags.data),
|
|
11992
12159
|
order: str(parsed.flags.order),
|
|
12160
|
+
schedule: str(parsed.flags.schedule),
|
|
12161
|
+
privateStore: str(parsed.flags["private-store"]),
|
|
12162
|
+
flow: str(parsed.flags.flow),
|
|
12163
|
+
tool: str(parsed.flags.tool),
|
|
11993
12164
|
yes: parsed.flags.yes === true
|
|
11994
12165
|
}, io);
|
|
11995
12166
|
case "init": return init({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sayren",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "sayren CLI — 호스팅 스토어프론트(init·pull·deploy)와 서드파티 앱(app init·dev·deploy·release)",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"esbuild": "^0.28.2",
|
|
17
|
-
"@sayren/store-sdk": "^0.30.
|
|
17
|
+
"@sayren/store-sdk": "^0.30.13"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@biomejs/biome": "^2.5.14",
|