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.
Files changed (2) hide show
  1. package/dist/index.mjs +127 -7
  2. 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.7.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 server = await bundleServer(dir);
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 } = await loadBundle(dir, server);
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.7.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.12"
17
+ "@sayren/store-sdk": "^0.30.13"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@biomejs/biome": "^2.5.14",