sayren 0.7.0 → 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 +127 -7
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1037,7 +1037,7 @@ const cli_app_handle_mismatch = ((inputs, options = {}) => {
|
|
|
1037
1037
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1038
1038
|
/** @typedef {{ events: NonNullable<unknown> }} Cli_App_Handler_MissingInputs */
|
|
1039
1039
|
const ko_cli_app_handler_missing = (i) => {
|
|
1040
|
-
return `매니페스트(events·schedules·http.public)에 있지만 코드에 함수가 없습니다: ${i?.events} (HANDLER_MISSING)`;
|
|
1040
|
+
return `매니페스트(events·schedules·http.public·flow.notifications·mcp.tools)에 있지만 코드에 함수가 없습니다: ${i?.events} (HANDLER_MISSING)`;
|
|
1041
1041
|
};
|
|
1042
1042
|
/**
|
|
1043
1043
|
|
|
@@ -5257,6 +5257,8 @@ const ko_cli_usage = (i) => {
|
|
|
5257
5257
|
--data <json> app trigger: 이벤트 데이터
|
|
5258
5258
|
--order <orderId> app trigger: 주문 id를 데이터에 넣습니다
|
|
5259
5259
|
--schedule <이름> app trigger: 이벤트 대신 예약 작업을 실행합니다
|
|
5260
|
+
--flow <이름> app trigger: 이벤트 대신 흐름 알림을 보냅니다(--data는 알림 값)
|
|
5261
|
+
--tool <이름> app trigger: 이벤트 대신 에이전트 도구를 부릅니다(--data는 입력)
|
|
5260
5262
|
--lang <로케일> 메시지 언어(기본: SAYREN_LANG → 시스템 로케일 → ko)
|
|
5261
5263
|
|
|
5262
5264
|
종료 코드: 0 성공 · 1 실패 · 3 승인 대기(에이전트 토큰)`;
|
|
@@ -9180,7 +9182,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9180
9182
|
|
|
9181
9183
|
//#endregion
|
|
9182
9184
|
//#region package.json
|
|
9183
|
-
var version = "0.
|
|
9185
|
+
var version = "0.8.0";
|
|
9184
9186
|
|
|
9185
9187
|
//#endregion
|
|
9186
9188
|
//#region src/client.ts
|
|
@@ -9395,7 +9397,9 @@ async function loadBundle(dir, server) {
|
|
|
9395
9397
|
meta: {
|
|
9396
9398
|
events: [...meta.events],
|
|
9397
9399
|
http: [...meta.http],
|
|
9398
|
-
schedules: [...meta.schedules ?? []]
|
|
9400
|
+
schedules: [...meta.schedules ?? []],
|
|
9401
|
+
flows: [...meta.flows ?? []],
|
|
9402
|
+
tools: [...meta.tools ?? []]
|
|
9399
9403
|
}
|
|
9400
9404
|
};
|
|
9401
9405
|
}
|
|
@@ -9436,17 +9440,37 @@ function collectListing(dir, manifest) {
|
|
|
9436
9440
|
};
|
|
9437
9441
|
}
|
|
9438
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
|
+
};
|
|
9439
9454
|
async function verifyApp(dir) {
|
|
9440
9455
|
const { raw, manifest } = loadManifest(dir);
|
|
9441
|
-
const
|
|
9456
|
+
const external = manifest.runtime === "external";
|
|
9457
|
+
const server = external ? EXTERNAL_STUB_SERVER : await bundleServer(dir);
|
|
9442
9458
|
if (APP_SECRET_PATTERNS.find((pattern) => pattern.test(server))) throw new CliError(cli_app_secret_in_source());
|
|
9443
9459
|
const gzipBytes = gzipSync(server).byteLength;
|
|
9444
9460
|
if (gzipBytes > APP_SERVER_BUNDLE_MAX_GZIP_BYTES) throw new CliError(cli_app_bundle_too_large({ size: formatMb(gzipBytes) }));
|
|
9445
|
-
const { app, meta } =
|
|
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);
|
|
9446
9468
|
const httpKeys = new Set(meta.http.map((key) => key.trim().replace(/\s+/g, " ")));
|
|
9447
|
-
const missing = [
|
|
9469
|
+
const missing = external ? [] : [
|
|
9448
9470
|
...manifest.events.filter((event) => !meta.events.includes(event)),
|
|
9449
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)),
|
|
9450
9474
|
...(manifest.http?.public ?? []).filter((key) => !httpKeys.has(key))
|
|
9451
9475
|
];
|
|
9452
9476
|
if (missing.length) throw new CliError(cli_app_handler_missing({ events: missing.join(", ") }));
|
|
@@ -9825,6 +9849,16 @@ function devEnvelope(input) {
|
|
|
9825
9849
|
role: "OWNER"
|
|
9826
9850
|
} } : {},
|
|
9827
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
|
+
} } : {},
|
|
9828
9862
|
...input.event ? { event: {
|
|
9829
9863
|
id: `evt_dev_${devSeq}`,
|
|
9830
9864
|
type: input.event.type,
|
|
@@ -9900,6 +9934,47 @@ async function appDev(flags, io) {
|
|
|
9900
9934
|
try {
|
|
9901
9935
|
if (req.method === "POST" && url.pathname === "/_trigger") {
|
|
9902
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
|
+
}
|
|
9903
9978
|
if (body.schedule) {
|
|
9904
9979
|
const { settings: settings$1, secrets: secrets$1 } = local();
|
|
9905
9980
|
const result$1 = await invokeLocal(app, devEnvelope({
|
|
@@ -10019,6 +10094,47 @@ async function appDev(flags, io) {
|
|
|
10019
10094
|
}
|
|
10020
10095
|
async function appTrigger(type, flags, io) {
|
|
10021
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
|
+
}
|
|
10022
10138
|
if (flags.schedule) {
|
|
10023
10139
|
const res$1 = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
10024
10140
|
method: "POST",
|
|
@@ -11954,7 +12070,9 @@ const VALUE_FLAGS = new Set([
|
|
|
11954
12070
|
"data",
|
|
11955
12071
|
"order",
|
|
11956
12072
|
"schedule",
|
|
11957
|
-
"private-store"
|
|
12073
|
+
"private-store",
|
|
12074
|
+
"flow",
|
|
12075
|
+
"tool"
|
|
11958
12076
|
]);
|
|
11959
12077
|
const BOOL_FLAGS = new Set([
|
|
11960
12078
|
"force",
|
|
@@ -12041,6 +12159,8 @@ async function main(argv, io) {
|
|
|
12041
12159
|
order: str(parsed.flags.order),
|
|
12042
12160
|
schedule: str(parsed.flags.schedule),
|
|
12043
12161
|
privateStore: str(parsed.flags["private-store"]),
|
|
12162
|
+
flow: str(parsed.flags.flow),
|
|
12163
|
+
tool: str(parsed.flags.tool),
|
|
12044
12164
|
yes: parsed.flags.yes === true
|
|
12045
12165
|
}, io);
|
|
12046
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",
|