sonamu 0.7.12 → 0.7.13

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 (55) hide show
  1. package/dist/api/config.d.ts +0 -3
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +1 -1
  4. package/dist/api/sonamu.d.ts.map +1 -1
  5. package/dist/api/sonamu.js +14 -4
  6. package/dist/bin/cli.js +2 -58
  7. package/dist/syncer/api-parser.d.ts.map +1 -1
  8. package/dist/syncer/api-parser.js +3 -2
  9. package/dist/syncer/syncer.d.ts +2 -1
  10. package/dist/syncer/syncer.d.ts.map +1 -1
  11. package/dist/syncer/syncer.js +17 -18
  12. package/dist/types/types.d.ts +1 -1
  13. package/dist/ui/ai-api.d.ts +1 -0
  14. package/dist/ui/ai-api.d.ts.map +1 -0
  15. package/dist/ui/ai-api.js +50 -0
  16. package/dist/ui/ai-client.d.ts +1 -0
  17. package/dist/ui/ai-client.d.ts.map +1 -0
  18. package/dist/ui/ai-client.js +438 -0
  19. package/dist/ui/api.d.ts +3 -0
  20. package/dist/ui/api.d.ts.map +1 -0
  21. package/dist/ui/api.js +680 -0
  22. package/dist/ui-web/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
  23. package/dist/ui-web/assets/brand-icons-F3SPCeH1.woff +0 -0
  24. package/dist/ui-web/assets/brand-icons-XL9sxUpA.woff2 +0 -0
  25. package/dist/ui-web/assets/brand-icons-sqJ2Pg7a.eot +0 -0
  26. package/dist/ui-web/assets/brand-icons-ubhWoxly.ttf +0 -0
  27. package/dist/ui-web/assets/flags-DOLqOU7Y.png +0 -0
  28. package/dist/ui-web/assets/icons-BOCtAERH.woff +0 -0
  29. package/dist/ui-web/assets/icons-CHzK1VD9.eot +0 -0
  30. package/dist/ui-web/assets/icons-D29ZQHHw.ttf +0 -0
  31. package/dist/ui-web/assets/icons-Du6TOHnR.woff2 +0 -0
  32. package/dist/ui-web/assets/icons-RwhydX30.svg +1518 -0
  33. package/dist/ui-web/assets/index-CpaB9P6g.css +1 -0
  34. package/dist/ui-web/assets/index-J9MCfjCd.js +95 -0
  35. package/dist/ui-web/assets/outline-icons-BfdLr8tr.svg +366 -0
  36. package/dist/ui-web/assets/outline-icons-DD8jm0uy.ttf +0 -0
  37. package/dist/ui-web/assets/outline-icons-DInHoiqI.woff2 +0 -0
  38. package/dist/ui-web/assets/outline-icons-LX8adJ4n.eot +0 -0
  39. package/dist/ui-web/assets/outline-icons-aQ88nltS.woff +0 -0
  40. package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +1 -0
  41. package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +1 -0
  42. package/dist/ui-web/index.html +13 -0
  43. package/dist/ui-web/vite.svg +1 -0
  44. package/dist/vector/embedding.d.ts.map +1 -1
  45. package/dist/vector/embedding.js +7 -7
  46. package/package.json +7 -5
  47. package/src/api/config.ts +0 -3
  48. package/src/api/sonamu.ts +17 -4
  49. package/src/bin/cli.ts +1 -67
  50. package/src/syncer/api-parser.ts +2 -1
  51. package/src/syncer/syncer.ts +20 -21
  52. package/src/ui/ai-api.ts +60 -0
  53. package/src/ui/ai-client.ts +499 -0
  54. package/src/ui/api.ts +786 -0
  55. package/src/vector/embedding.ts +8 -6
@@ -1,6 +1,4 @@
1
- import { createOpenAI } from "@ai-sdk/openai";
2
1
  import { embedMany } from "ai";
3
- import { VoyageAIClient } from "voyageai";
4
2
  import { Sonamu } from "../api/sonamu.js";
5
3
  import { DEFAULT_VECTOR_CONFIG } from "./config.js";
6
4
  /**
@@ -34,7 +32,8 @@ import { DEFAULT_VECTOR_CONFIG } from "./config.js";
34
32
  }
35
33
  /**
36
34
  * Voyage AI 클라이언트 초기화
37
- */ getVoyageClient() {
35
+ */ async getVoyageClient() {
36
+ const { VoyageAIClient } = await import("voyageai");
38
37
  const apiKey = Sonamu.secrets?.voyage_api_key ?? process.env.VOYAGE_API_KEY;
39
38
  if (!apiKey) {
40
39
  throw new Error("VOYAGE_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
@@ -45,7 +44,8 @@ import { DEFAULT_VECTOR_CONFIG } from "./config.js";
45
44
  }
46
45
  /**
47
46
  * OpenAI provider 생성
48
- */ getOpenAIProvider() {
47
+ */ async getOpenAIProvider() {
48
+ const { createOpenAI } = await import("@ai-sdk/openai");
49
49
  const apiKey = Sonamu.secrets?.openai_api_key ?? process.env.OPENAI_API_KEY;
50
50
  if (!apiKey) {
51
51
  throw new Error("OPENAI_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
@@ -85,7 +85,7 @@ import { DEFAULT_VECTOR_CONFIG } from "./config.js";
85
85
  /**
86
86
  * Voyage AI 임베딩
87
87
  */ async embedVoyage(texts, inputType) {
88
- const client = this.getVoyageClient();
88
+ const client = await this.getVoyageClient();
89
89
  const voyageConfig = this.config.voyage;
90
90
  const response = await client.embed({
91
91
  input: texts,
@@ -104,7 +104,7 @@ import { DEFAULT_VECTOR_CONFIG } from "./config.js";
104
104
  /**
105
105
  * OpenAI 임베딩
106
106
  */ async embedOpenAI(texts) {
107
- const openai = this.getOpenAIProvider();
107
+ const openai = await this.getOpenAIProvider();
108
108
  const openaiConfig = this.config.openai;
109
109
  const model = openai.embeddingModel(openaiConfig.model);
110
110
  const { embeddings, usage } = await embedMany({
@@ -125,4 +125,4 @@ import { DEFAULT_VECTOR_CONFIG } from "./config.js";
125
125
  }
126
126
  export const Embedding = new EmbeddingClass();
127
127
 
128
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZWN0b3IvZW1iZWRkaW5nLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNyZWF0ZU9wZW5BSSwgdHlwZSBPcGVuQUlQcm92aWRlciB9IGZyb20gXCJAYWktc2RrL29wZW5haVwiO1xuaW1wb3J0IHsgdHlwZSBFbWJlZGRpbmdNb2RlbCwgZW1iZWRNYW55IH0gZnJvbSBcImFpXCI7XG5pbXBvcnQgeyBWb3lhZ2VBSUNsaWVudCB9IGZyb20gXCJ2b3lhZ2VhaVwiO1xuaW1wb3J0IHsgU29uYW11IH0gZnJvbSBcIi4uL2FwaS9zb25hbXVcIjtcbmltcG9ydCB7IERFRkFVTFRfVkVDVE9SX0NPTkZJRyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHR5cGUge1xuICBFbWJlZGRpbmdQcm92aWRlcixcbiAgRW1iZWRkaW5nUmVzdWx0LFxuICBQcm9ncmVzc0NhbGxiYWNrLFxuICBWZWN0b3JDb25maWcsXG4gIFZlY3RvcklucHV0VHlwZSxcbn0gZnJvbSBcIi4vdHlwZXNcIjtcblxuLyoqXG4gKiDsnoTrsqDrlKkg7YG065287J207Ja47Yq4XG4gKiBWb3lhZ2UgQUnsmYAgT3BlbkFJIOyehOuyoOuUqeydhCBTREsg67Cp7Iud7Jy866GcIO2Gte2VqSDsp4Dsm5BcbiAqL1xuZXhwb3J0IGNsYXNzIEVtYmVkZGluZ0NsYXNzIHtcbiAgcHJpdmF0ZSBjb25maWc6IFZlY3RvckNvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IFBhcnRpYWw8VmVjdG9yQ29uZmlnPiA9IHt9KSB7XG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICB2b3lhZ2U6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnZveWFnZSwgLi4uY29uZmlnLnZveWFnZSB9LFxuICAgICAgb3BlbmFpOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5vcGVuYWksIC4uLmNvbmZpZy5vcGVuYWkgfSxcbiAgICAgIGNodW5raW5nOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5jaHVua2luZywgLi4uY29uZmlnLmNodW5raW5nIH0sXG4gICAgICBzZWFyY2g6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnNlYXJjaCwgLi4uY29uZmlnLnNlYXJjaCB9LFxuICAgICAgcGd2ZWN0b3I6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnBndmVjdG9yLCAuLi5jb25maWcucGd2ZWN0b3IgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZveWFnZSBBSSDtgbTrnbzsnbTslrjtirgg7LSI6riw7ZmUXG4gICAqL1xuICBwcml2YXRlIGdldFZveWFnZUNsaWVudCgpOiBWb3lhZ2VBSUNsaWVudCB7XG4gICAgY29uc3QgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/LnZveWFnZV9hcGlfa2V5ID8/IHByb2Nlc3MuZW52LlZPWUFHRV9BUElfS0VZO1xuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWT1lBR0VfQVBJX0tFWeqwgCDshKTsoJXrkJjsp4Ag7JWK7JWY7Iq164uI64ukLiDtmZjqsr3rs4DsiJjrpbwg7ZmV7J247ZWY7IS47JqULlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBWb3lhZ2VBSUNsaWVudCh7IGFwaUtleSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcGVuQUkgcHJvdmlkZXIg7IOd7ISxXG4gICAqL1xuICBwcml2YXRlIGdldE9wZW5BSVByb3ZpZGVyKCk6IE9wZW5BSVByb3ZpZGVyIHtcbiAgICBjb25zdCBhcGlLZXkgPSBTb25hbXUuc2VjcmV0cz8ub3BlbmFpX2FwaV9rZXkgPz8gcHJvY2Vzcy5lbnYuT1BFTkFJX0FQSV9LRVk7XG4gICAgaWYgKCFhcGlLZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIk9QRU5BSV9BUElfS0VZ6rCAIOyEpOygleuQmOyngCDslYrslZjsirXri4jri6QuIO2ZmOqyveuzgOyImOulvCDtmZXsnbjtlZjshLjsmpQuXCIpO1xuICAgIH1cbiAgICByZXR1cm4gY3JlYXRlT3BlbkFJKHsgYXBpS2V5IH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIO2FjeyKpO2KuCDsnoTrsqDrlKkg7IOd7ISxXG4gICAqIEBwYXJhbSB0ZXh0cyAtIOyehOuyoOuUqe2VoCDthY3siqTtirgg67Cw7Je0IChiYXRjaFNpemXsnbTsg4Eg7IucIOyekOuPmSDrtoTtlaApXG4gICAqIEBwYXJhbSBwcm92aWRlciAtICd2b3lhZ2UnIHwgJ29wZW5haSdcbiAgICogQHBhcmFtIGlucHV0VHlwZSAtICdkb2N1bWVudCcgfCAncXVlcnknIChWb3lhZ2UgQUnrp4wg7ZW064u5KVxuICAgKiBAcGFyYW0gb25Qcm9ncmVzcyAtIOynhO2WieuloCDsvZzrsLFcbiAgICovXG4gIGFzeW5jIGVtYmVkKFxuICAgIHRleHRzOiBzdHJpbmdbXSxcbiAgICBwcm92aWRlcjogRW1iZWRkaW5nUHJvdmlkZXIsXG4gICAgaW5wdXRUeXBlOiBWZWN0b3JJbnB1dFR5cGUgPSBcImRvY3VtZW50XCIsXG4gICAgb25Qcm9ncmVzcz86IFByb2dyZXNzQ2FsbGJhY2ssXG4gICk6IFByb21pc2U8RW1iZWRkaW5nUmVzdWx0W10+IHtcbiAgICBjb25zdCBtYXhCYXRjaFNpemUgPVxuICAgICAgcHJvdmlkZXIgPT09IFwidm95YWdlXCIgPyB0aGlzLmNvbmZpZy52b3lhZ2UuYmF0Y2hTaXplIDogdGhpcy5jb25maWcub3BlbmFpLmJhdGNoU2l6ZTtcblxuICAgIC8vIGJhdGNoU2l6ZeydtO2VmOuptCDrsJTroZwg7Zi47LacXG4gICAgaWYgKHRleHRzLmxlbmd0aCA8PSBtYXhCYXRjaFNpemUpIHtcbiAgICAgIHJldHVybiBwcm92aWRlciA9PT0gXCJ2b3lhZ2VcIlxuICAgICAgICA/IGF3YWl0IHRoaXMuZW1iZWRWb3lhZ2UodGV4dHMsIGlucHV0VHlwZSlcbiAgICAgICAgOiBhd2FpdCB0aGlzLmVtYmVkT3BlbkFJKHRleHRzKTtcbiAgICB9XG5cbiAgICAvLyBiYXRjaFNpemXsnbTsg4HsnbTrqbQg7J6Q64+Z7Jy866GcIOuCmOuIoOyEnCDsspjrpqxcbiAgICBjb25zdCBiYXRjaGVzID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogTWF0aC5jZWlsKHRleHRzLmxlbmd0aCAvIG1heEJhdGNoU2l6ZSkgfSwgKF8sIGkpID0+XG4gICAgICB0ZXh0cy5zbGljZShpICogbWF4QmF0Y2hTaXplLCAoaSArIDEpICogbWF4QmF0Y2hTaXplKSxcbiAgICApO1xuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgYmF0Y2hlcy5tYXAoKGJhdGNoKSA9PlxuICAgICAgICBwcm92aWRlciA9PT0gXCJ2b3lhZ2VcIiA/IHRoaXMuZW1iZWRWb3lhZ2UoYmF0Y2gsIGlucHV0VHlwZSkgOiB0aGlzLmVtYmVkT3BlbkFJKGJhdGNoKSxcbiAgICAgICksXG4gICAgKTtcblxuICAgIG9uUHJvZ3Jlc3M/Lih0ZXh0cy5sZW5ndGgsIHRleHRzLmxlbmd0aCk7XG4gICAgcmV0dXJuIHJlc3VsdHMuZmxhdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIOuLqOydvCDthY3siqTtirgg7J6E67Kg65SpICjtjrjsnZgg66mU7ISc65OcKVxuICAgKi9cbiAgYXN5bmMgZW1iZWRPbmUoXG4gICAgdGV4dDogc3RyaW5nLFxuICAgIHByb3ZpZGVyOiBFbWJlZGRpbmdQcm92aWRlcixcbiAgICBpbnB1dFR5cGU6IFZlY3RvcklucHV0VHlwZSA9IFwiZG9jdW1lbnRcIixcbiAgKTogUHJvbWlzZTxFbWJlZGRpbmdSZXN1bHQ+IHtcbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgdGhpcy5lbWJlZChbdGV4dF0sIHByb3ZpZGVyLCBpbnB1dFR5cGUpO1xuICAgIHJldHVybiByZXN1bHRzWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIFZveWFnZSBBSSDsnoTrsqDrlKlcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZW1iZWRWb3lhZ2UoXG4gICAgdGV4dHM6IHN0cmluZ1tdLFxuICAgIGlucHV0VHlwZTogVmVjdG9ySW5wdXRUeXBlLFxuICApOiBQcm9taXNlPEVtYmVkZGluZ1Jlc3VsdFtdPiB7XG4gICAgY29uc3QgY2xpZW50ID0gdGhpcy5nZXRWb3lhZ2VDbGllbnQoKTtcbiAgICBjb25zdCB2b3lhZ2VDb25maWcgPSB0aGlzLmNvbmZpZy52b3lhZ2U7XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5lbWJlZCh7XG4gICAgICBpbnB1dDogdGV4dHMsXG4gICAgICBtb2RlbDogdm95YWdlQ29uZmlnLm1vZGVsLFxuICAgICAgaW5wdXRUeXBlOiBpbnB1dFR5cGUsXG4gICAgfSk7XG4gICAgaWYgKCFyZXNwb25zZS5kYXRhKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWb3lhZ2UgQVBJOiDsnZHri7Ug642w7J207YSw6rCAIOyXhuyKteuLiOuLpC5cIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlLmRhdGEubWFwKChpdGVtKSA9PiAoe1xuICAgICAgZW1iZWRkaW5nOiBpdGVtLmVtYmVkZGluZyA/PyBbXSxcbiAgICAgIG1vZGVsOiB2b3lhZ2VDb25maWcubW9kZWwsXG4gICAgICB0b2tlbkNvdW50OiByZXNwb25zZS51c2FnZT8udG90YWxUb2tlbnMgPz8gMCxcbiAgICB9KSk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbkFJIOyehOuyoOuUqVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbWJlZE9wZW5BSSh0ZXh0czogc3RyaW5nW10pOiBQcm9taXNlPEVtYmVkZGluZ1Jlc3VsdFtdPiB7XG4gICAgY29uc3Qgb3BlbmFpID0gdGhpcy5nZXRPcGVuQUlQcm92aWRlcigpO1xuICAgIGNvbnN0IG9wZW5haUNvbmZpZyA9IHRoaXMuY29uZmlnLm9wZW5haTtcbiAgICBjb25zdCBtb2RlbCA9IG9wZW5haS5lbWJlZGRpbmdNb2RlbChvcGVuYWlDb25maWcubW9kZWwpO1xuXG4gICAgY29uc3QgeyBlbWJlZGRpbmdzLCB1c2FnZSB9ID0gYXdhaXQgZW1iZWRNYW55KHtcbiAgICAgIG1vZGVsOiBtb2RlbCBhcyBFbWJlZGRpbmdNb2RlbCxcbiAgICAgIHZhbHVlczogdGV4dHMsXG4gICAgfSk7XG5cbiAgICByZXR1cm4gZW1iZWRkaW5ncy5tYXAoKGVtYmVkZGluZykgPT4gKHtcbiAgICAgIGVtYmVkZGluZyxcbiAgICAgIG1vZGVsOiBvcGVuYWlDb25maWcubW9kZWwsXG4gICAgICB0b2tlbkNvdW50OiB1c2FnZT8udG9rZW5zID8/IDAsXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIOyehOuyoOuUqSBwcm92aWRlcuydmCDssKjsm5Ag7IiYIOuwmO2ZmFxuICAgKi9cbiAgZ2V0RGltZW5zaW9ucyhwcm92aWRlcjogRW1iZWRkaW5nUHJvdmlkZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBwcm92aWRlciA9PT0gXCJ2b3lhZ2VcIiA/IHRoaXMuY29uZmlnLnZveWFnZS5kaW1lbnNpb25zIDogdGhpcy5jb25maWcub3BlbmFpLmRpbWVuc2lvbnM7XG4gIH1cbn1cbmV4cG9ydCBjb25zdCBFbWJlZGRpbmcgPSBuZXcgRW1iZWRkaW5nQ2xhc3MoKTtcbiJdLCJuYW1lcyI6WyJjcmVhdGVPcGVuQUkiLCJlbWJlZE1hbnkiLCJWb3lhZ2VBSUNsaWVudCIsIlNvbmFtdSIsIkRFRkFVTFRfVkVDVE9SX0NPTkZJRyIsIkVtYmVkZGluZ0NsYXNzIiwiY29uZmlnIiwidm95YWdlIiwib3BlbmFpIiwiY2h1bmtpbmciLCJzZWFyY2giLCJwZ3ZlY3RvciIsImdldFZveWFnZUNsaWVudCIsImFwaUtleSIsInNlY3JldHMiLCJ2b3lhZ2VfYXBpX2tleSIsInByb2Nlc3MiLCJlbnYiLCJWT1lBR0VfQVBJX0tFWSIsIkVycm9yIiwiZ2V0T3BlbkFJUHJvdmlkZXIiLCJvcGVuYWlfYXBpX2tleSIsIk9QRU5BSV9BUElfS0VZIiwiZW1iZWQiLCJ0ZXh0cyIsInByb3ZpZGVyIiwiaW5wdXRUeXBlIiwib25Qcm9ncmVzcyIsIm1heEJhdGNoU2l6ZSIsImJhdGNoU2l6ZSIsImxlbmd0aCIsImVtYmVkVm95YWdlIiwiZW1iZWRPcGVuQUkiLCJiYXRjaGVzIiwiQXJyYXkiLCJmcm9tIiwiTWF0aCIsImNlaWwiLCJfIiwiaSIsInNsaWNlIiwicmVzdWx0cyIsIlByb21pc2UiLCJhbGwiLCJtYXAiLCJiYXRjaCIsImZsYXQiLCJlbWJlZE9uZSIsInRleHQiLCJjbGllbnQiLCJ2b3lhZ2VDb25maWciLCJyZXNwb25zZSIsImlucHV0IiwibW9kZWwiLCJkYXRhIiwiaXRlbSIsImVtYmVkZGluZyIsInRva2VuQ291bnQiLCJ1c2FnZSIsInRvdGFsVG9rZW5zIiwib3BlbmFpQ29uZmlnIiwiZW1iZWRkaW5nTW9kZWwiLCJlbWJlZGRpbmdzIiwidmFsdWVzIiwidG9rZW5zIiwiZ2V0RGltZW5zaW9ucyIsImRpbWVuc2lvbnMiLCJFbWJlZGRpbmciXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLFlBQVksUUFBNkIsaUJBQWlCO0FBQ25FLFNBQThCQyxTQUFTLFFBQVEsS0FBSztBQUNwRCxTQUFTQyxjQUFjLFFBQVEsV0FBVztBQUMxQyxTQUFTQyxNQUFNLFFBQVEsbUJBQWdCO0FBQ3ZDLFNBQVNDLHFCQUFxQixRQUFRLGNBQVc7QUFTakQ7OztDQUdDLEdBQ0QsT0FBTyxNQUFNQztJQUNIQyxPQUFxQjtJQUU3QixZQUFZQSxTQUFnQyxDQUFDLENBQUMsQ0FBRTtRQUM5QyxJQUFJLENBQUNBLE1BQU0sR0FBRztZQUNaQyxRQUFRO2dCQUFFLEdBQUdILHNCQUFzQkcsTUFBTTtnQkFBRSxHQUFHRCxPQUFPQyxNQUFNO1lBQUM7WUFDNURDLFFBQVE7Z0JBQUUsR0FBR0osc0JBQXNCSSxNQUFNO2dCQUFFLEdBQUdGLE9BQU9FLE1BQU07WUFBQztZQUM1REMsVUFBVTtnQkFBRSxHQUFHTCxzQkFBc0JLLFFBQVE7Z0JBQUUsR0FBR0gsT0FBT0csUUFBUTtZQUFDO1lBQ2xFQyxRQUFRO2dCQUFFLEdBQUdOLHNCQUFzQk0sTUFBTTtnQkFBRSxHQUFHSixPQUFPSSxNQUFNO1lBQUM7WUFDNURDLFVBQVU7Z0JBQUUsR0FBR1Asc0JBQXNCTyxRQUFRO2dC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
128
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZWN0b3IvZW1iZWRkaW5nLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgT3BlbkFJUHJvdmlkZXIgfSBmcm9tIFwiQGFpLXNkay9vcGVuYWlcIjtcbmltcG9ydCB7IHR5cGUgRW1iZWRkaW5nTW9kZWwsIGVtYmVkTWFueSB9IGZyb20gXCJhaVwiO1xuaW1wb3J0IHR5cGUgeyBWb3lhZ2VBSUNsaWVudCB9IGZyb20gXCJ2b3lhZ2VhaVwiO1xuaW1wb3J0IHsgU29uYW11IH0gZnJvbSBcIi4uL2FwaS9zb25hbXVcIjtcbmltcG9ydCB7IERFRkFVTFRfVkVDVE9SX0NPTkZJRyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHR5cGUge1xuICBFbWJlZGRpbmdQcm92aWRlcixcbiAgRW1iZWRkaW5nUmVzdWx0LFxuICBQcm9ncmVzc0NhbGxiYWNrLFxuICBWZWN0b3JDb25maWcsXG4gIFZlY3RvcklucHV0VHlwZSxcbn0gZnJvbSBcIi4vdHlwZXNcIjtcblxuLyoqXG4gKiDsnoTrsqDrlKkg7YG065287J207Ja47Yq4XG4gKiBWb3lhZ2UgQUnsmYAgT3BlbkFJIOyehOuyoOuUqeydhCBTREsg67Cp7Iud7Jy866GcIO2Gte2VqSDsp4Dsm5BcbiAqL1xuZXhwb3J0IGNsYXNzIEVtYmVkZGluZ0NsYXNzIHtcbiAgcHJpdmF0ZSBjb25maWc6IFZlY3RvckNvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IFBhcnRpYWw8VmVjdG9yQ29uZmlnPiA9IHt9KSB7XG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICB2b3lhZ2U6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnZveWFnZSwgLi4uY29uZmlnLnZveWFnZSB9LFxuICAgICAgb3BlbmFpOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5vcGVuYWksIC4uLmNvbmZpZy5vcGVuYWkgfSxcbiAgICAgIGNodW5raW5nOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5jaHVua2luZywgLi4uY29uZmlnLmNodW5raW5nIH0sXG4gICAgICBzZWFyY2g6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnNlYXJjaCwgLi4uY29uZmlnLnNlYXJjaCB9LFxuICAgICAgcGd2ZWN0b3I6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnBndmVjdG9yLCAuLi5jb25maWcucGd2ZWN0b3IgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZveWFnZSBBSSDtgbTrnbzsnbTslrjtirgg7LSI6riw7ZmUXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldFZveWFnZUNsaWVudCgpOiBQcm9taXNlPFZveWFnZUFJQ2xpZW50PiB7XG4gICAgY29uc3QgeyBWb3lhZ2VBSUNsaWVudCB9ID0gYXdhaXQgaW1wb3J0KFwidm95YWdlYWlcIik7XG4gICAgY29uc3QgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/LnZveWFnZV9hcGlfa2V5ID8/IHByb2Nlc3MuZW52LlZPWUFHRV9BUElfS0VZO1xuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWT1lBR0VfQVBJX0tFWeqwgCDshKTsoJXrkJjsp4Ag7JWK7JWY7Iq164uI64ukLiDtmZjqsr3rs4DsiJjrpbwg7ZmV7J247ZWY7IS47JqULlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBWb3lhZ2VBSUNsaWVudCh7IGFwaUtleSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcGVuQUkgcHJvdmlkZXIg7IOd7ISxXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldE9wZW5BSVByb3ZpZGVyKCk6IFByb21pc2U8T3BlbkFJUHJvdmlkZXI+IHtcbiAgICBjb25zdCB7IGNyZWF0ZU9wZW5BSSB9ID0gYXdhaXQgaW1wb3J0KFwiQGFpLXNkay9vcGVuYWlcIik7XG4gICAgY29uc3QgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/Lm9wZW5haV9hcGlfa2V5ID8/IHByb2Nlc3MuZW52Lk9QRU5BSV9BUElfS0VZO1xuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJPUEVOQUlfQVBJX0tFWeqwgCDshKTsoJXrkJjsp4Ag7JWK7JWY7Iq164uI64ukLiDtmZjqsr3rs4DsiJjrpbwg7ZmV7J247ZWY7IS47JqULlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIGNyZWF0ZU9wZW5BSSh7IGFwaUtleSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiDthY3siqTtirgg7J6E67Kg65SpIOyDneyEsVxuICAgKiBAcGFyYW0gdGV4dHMgLSDsnoTrsqDrlKntlaAg7YWN7Iqk7Yq4IOuwsOyXtCAoYmF0Y2hTaXpl7J207IOBIOyLnCDsnpDrj5kg67aE7ZWgKVxuICAgKiBAcGFyYW0gcHJvdmlkZXIgLSAndm95YWdlJyB8ICdvcGVuYWknXG4gICAqIEBwYXJhbSBpbnB1dFR5cGUgLSAnZG9jdW1lbnQnIHwgJ3F1ZXJ5JyAoVm95YWdlIEFJ66eMIO2VtOuLuSlcbiAgICogQHBhcmFtIG9uUHJvZ3Jlc3MgLSDsp4TtlonrpaAg7L2c67CxXG4gICAqL1xuICBhc3luYyBlbWJlZChcbiAgICB0ZXh0czogc3RyaW5nW10sXG4gICAgcHJvdmlkZXI6IEVtYmVkZGluZ1Byb3ZpZGVyLFxuICAgIGlucHV0VHlwZTogVmVjdG9ySW5wdXRUeXBlID0gXCJkb2N1bWVudFwiLFxuICAgIG9uUHJvZ3Jlc3M/OiBQcm9ncmVzc0NhbGxiYWNrLFxuICApOiBQcm9taXNlPEVtYmVkZGluZ1Jlc3VsdFtdPiB7XG4gICAgY29uc3QgbWF4QmF0Y2hTaXplID1cbiAgICAgIHByb3ZpZGVyID09PSBcInZveWFnZVwiID8gdGhpcy5jb25maWcudm95YWdlLmJhdGNoU2l6ZSA6IHRoaXMuY29uZmlnLm9wZW5haS5iYXRjaFNpemU7XG5cbiAgICAvLyBiYXRjaFNpemXsnbTtlZjrqbQg67CU66GcIO2YuOy2nFxuICAgIGlmICh0ZXh0cy5sZW5ndGggPD0gbWF4QmF0Y2hTaXplKSB7XG4gICAgICByZXR1cm4gcHJvdmlkZXIgPT09IFwidm95YWdlXCJcbiAgICAgICAgPyBhd2FpdCB0aGlzLmVtYmVkVm95YWdlKHRleHRzLCBpbnB1dFR5cGUpXG4gICAgICAgIDogYXdhaXQgdGhpcy5lbWJlZE9wZW5BSSh0ZXh0cyk7XG4gICAgfVxuXG4gICAgLy8gYmF0Y2hTaXpl7J207IOB7J2066m0IOyekOuPmeycvOuhnCDrgpjriKDshJwg7LKY66asXG4gICAgY29uc3QgYmF0Y2hlcyA9IEFycmF5LmZyb20oeyBsZW5ndGg6IE1hdGguY2VpbCh0ZXh0cy5sZW5ndGggLyBtYXhCYXRjaFNpemUpIH0sIChfLCBpKSA9PlxuICAgICAgdGV4dHMuc2xpY2UoaSAqIG1heEJhdGNoU2l6ZSwgKGkgKyAxKSAqIG1heEJhdGNoU2l6ZSksXG4gICAgKTtcblxuICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIGJhdGNoZXMubWFwKChiYXRjaCkgPT5cbiAgICAgICAgcHJvdmlkZXIgPT09IFwidm95YWdlXCIgPyB0aGlzLmVtYmVkVm95YWdlKGJhdGNoLCBpbnB1dFR5cGUpIDogdGhpcy5lbWJlZE9wZW5BSShiYXRjaCksXG4gICAgICApLFxuICAgICk7XG5cbiAgICBvblByb2dyZXNzPy4odGV4dHMubGVuZ3RoLCB0ZXh0cy5sZW5ndGgpO1xuICAgIHJldHVybiByZXN1bHRzLmZsYXQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDri6jsnbwg7YWN7Iqk7Yq4IOyehOuyoOuUqSAo7Y647J2YIOuplOyEnOuTnClcbiAgICovXG4gIGFzeW5jIGVtYmVkT25lKFxuICAgIHRleHQ6IHN0cmluZyxcbiAgICBwcm92aWRlcjogRW1iZWRkaW5nUHJvdmlkZXIsXG4gICAgaW5wdXRUeXBlOiBWZWN0b3JJbnB1dFR5cGUgPSBcImRvY3VtZW50XCIsXG4gICk6IFByb21pc2U8RW1iZWRkaW5nUmVzdWx0PiB7XG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IHRoaXMuZW1iZWQoW3RleHRdLCBwcm92aWRlciwgaW5wdXRUeXBlKTtcbiAgICByZXR1cm4gcmVzdWx0c1swXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWb3lhZ2UgQUkg7J6E67Kg65SpXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGVtYmVkVm95YWdlKFxuICAgIHRleHRzOiBzdHJpbmdbXSxcbiAgICBpbnB1dFR5cGU6IFZlY3RvcklucHV0VHlwZSxcbiAgKTogUHJvbWlzZTxFbWJlZGRpbmdSZXN1bHRbXT4ge1xuICAgIGNvbnN0IGNsaWVudCA9IGF3YWl0IHRoaXMuZ2V0Vm95YWdlQ2xpZW50KCk7XG4gICAgY29uc3Qgdm95YWdlQ29uZmlnID0gdGhpcy5jb25maWcudm95YWdlO1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQuZW1iZWQoe1xuICAgICAgaW5wdXQ6IHRleHRzLFxuICAgICAgbW9kZWw6IHZveWFnZUNvbmZpZy5tb2RlbCxcbiAgICAgIGlucHV0VHlwZTogaW5wdXRUeXBlLFxuICAgIH0pO1xuICAgIGlmICghcmVzcG9uc2UuZGF0YSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVm95YWdlIEFQSTog7J2R64u1IOuNsOydtO2EsOqwgCDsl4bsirXri4jri6QuXCIpO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZS5kYXRhLm1hcCgoaXRlbSkgPT4gKHtcbiAgICAgIGVtYmVkZGluZzogaXRlbS5lbWJlZGRpbmcgPz8gW10sXG4gICAgICBtb2RlbDogdm95YWdlQ29uZmlnLm1vZGVsLFxuICAgICAgdG9rZW5Db3VudDogcmVzcG9uc2UudXNhZ2U/LnRvdGFsVG9rZW5zID8/IDAsXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW5BSSDsnoTrsqDrlKlcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZW1iZWRPcGVuQUkodGV4dHM6IHN0cmluZ1tdKTogUHJvbWlzZTxFbWJlZGRpbmdSZXN1bHRbXT4ge1xuICAgIGNvbnN0IG9wZW5haSA9IGF3YWl0IHRoaXMuZ2V0T3BlbkFJUHJvdmlkZXIoKTtcbiAgICBjb25zdCBvcGVuYWlDb25maWcgPSB0aGlzLmNvbmZpZy5vcGVuYWk7XG4gICAgY29uc3QgbW9kZWwgPSBvcGVuYWkuZW1iZWRkaW5nTW9kZWwob3BlbmFpQ29uZmlnLm1vZGVsKTtcblxuICAgIGNvbnN0IHsgZW1iZWRkaW5ncywgdXNhZ2UgfSA9IGF3YWl0IGVtYmVkTWFueSh7XG4gICAgICBtb2RlbDogbW9kZWwgYXMgRW1iZWRkaW5nTW9kZWwsXG4gICAgICB2YWx1ZXM6IHRleHRzLFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGVtYmVkZGluZ3MubWFwKChlbWJlZGRpbmcpID0+ICh7XG4gICAgICBlbWJlZGRpbmcsXG4gICAgICBtb2RlbDogb3BlbmFpQ29uZmlnLm1vZGVsLFxuICAgICAgdG9rZW5Db3VudDogdXNhZ2U/LnRva2VucyA/PyAwLFxuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDsnoTrsqDrlKkgcHJvdmlkZXLsnZgg7LCo7JuQIOyImCDrsJjtmZhcbiAgICovXG4gIGdldERpbWVuc2lvbnMocHJvdmlkZXI6IEVtYmVkZGluZ1Byb3ZpZGVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gcHJvdmlkZXIgPT09IFwidm95YWdlXCIgPyB0aGlzLmNvbmZpZy52b3lhZ2UuZGltZW5zaW9ucyA6IHRoaXMuY29uZmlnLm9wZW5haS5kaW1lbnNpb25zO1xuICB9XG59XG5leHBvcnQgY29uc3QgRW1iZWRkaW5nID0gbmV3IEVtYmVkZGluZ0NsYXNzKCk7XG4iXSwibmFtZXMiOlsiZW1iZWRNYW55IiwiU29uYW11IiwiREVGQVVMVF9WRUNUT1JfQ09ORklHIiwiRW1iZWRkaW5nQ2xhc3MiLCJjb25maWciLCJ2b3lhZ2UiLCJvcGVuYWkiLCJjaHVua2luZyIsInNlYXJjaCIsInBndmVjdG9yIiwiZ2V0Vm95YWdlQ2xpZW50IiwiVm95YWdlQUlDbGllbnQiLCJhcGlLZXkiLCJzZWNyZXRzIiwidm95YWdlX2FwaV9rZXkiLCJwcm9jZXNzIiwiZW52IiwiVk9ZQUdFX0FQSV9LRVkiLCJFcnJvciIsImdldE9wZW5BSVByb3ZpZGVyIiwiY3JlYXRlT3BlbkFJIiwib3BlbmFpX2FwaV9rZXkiLCJPUEVOQUlfQVBJX0tFWSIsImVtYmVkIiwidGV4dHMiLCJwcm92aWRlciIsImlucHV0VHlwZSIsIm9uUHJvZ3Jlc3MiLCJtYXhCYXRjaFNpemUiLCJiYXRjaFNpemUiLCJsZW5ndGgiLCJlbWJlZFZveWFnZSIsImVtYmVkT3BlbkFJIiwiYmF0Y2hlcyIsIkFycmF5IiwiZnJvbSIsIk1hdGgiLCJjZWlsIiwiXyIsImkiLCJzbGljZSIsInJlc3VsdHMiLCJQcm9taXNlIiwiYWxsIiwibWFwIiwiYmF0Y2giLCJmbGF0IiwiZW1iZWRPbmUiLCJ0ZXh0IiwiY2xpZW50Iiwidm95YWdlQ29uZmlnIiwicmVzcG9uc2UiLCJpbnB1dCIsIm1vZGVsIiwiZGF0YSIsIml0ZW0iLCJlbWJlZGRpbmciLCJ0b2tlbkNvdW50IiwidXNhZ2UiLCJ0b3RhbFRva2VucyIsIm9wZW5haUNvbmZpZyIsImVtYmVkZGluZ01vZGVsIiwiZW1iZWRkaW5ncyIsInZhbHVlcyIsInRva2VucyIsImdldERpbWVuc2lvbnMiLCJkaW1lbnNpb25zIiwiRW1iZWRkaW5nIl0sIm1hcHBpbmdzIjoiQUFDQSxTQUE4QkEsU0FBUyxRQUFRLEtBQUs7QUFFcEQsU0FBU0MsTUFBTSxRQUFRLG1CQUFnQjtBQUN2QyxTQUFTQyxxQkFBcUIsUUFBUSxjQUFXO0FBU2pEOzs7Q0FHQyxHQUNELE9BQU8sTUFBTUM7SUFDSEMsT0FBcUI7SUFFN0IsWUFBWUEsU0FBZ0MsQ0FBQyxDQUFDLENBQUU7UUFDOUMsSUFBSSxDQUFDQSxNQUFNLEdBQUc7WUFDWkMsUUFBUTtnQkFBRSxHQUFHSCxzQkFBc0JHLE1BQU07Z0JBQUUsR0FBR0QsT0FBT0MsTUFBTTtZQUFDO1lBQzVEQyxRQUFRO2dCQUFFLEdBQUdKLHNCQUFzQkksTUFBTTtnQkFBRSxHQUFHRixPQUFPRSxNQUFNO1lBQUM7WUFDNURDLFVBQVU7Z0JBQUUsR0FBR0wsc0JBQXNCSyxRQUFRO2dCQUFFLEdBQUdILE9BQU9HLFFBQVE7WUFBQztZQUNs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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonamu",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "description": "Sonamu — TypeScript Fullstack API Framework",
5
5
  "keywords": [
6
6
  "typescript",
@@ -68,6 +68,7 @@
68
68
  "knex": "^3.1.0",
69
69
  "mime-types": "^3.0.1",
70
70
  "minimatch": "^10.0.3",
71
+ "node-cron": "^4.2.1",
71
72
  "node-sql-parser": "^5.2.0",
72
73
  "pg": "^8.16.3",
73
74
  "prompts": "^2.4.2",
@@ -76,10 +77,9 @@
76
77
  "tsicli": "^1.0.5",
77
78
  "vitest": "^4.0.10",
78
79
  "zod": "^4.1.12",
79
- "node-cron": "^4.2.1",
80
80
  "@sonamu-kit/hmr-hook": "^0.4.1",
81
- "@sonamu-kit/ts-loader": "^2.1.3",
82
81
  "@sonamu-kit/tasks": "^0.0.1",
82
+ "@sonamu-kit/ts-loader": "^2.1.3",
83
83
  "@sonamu-kit/hmr-runner": "^0.1.1"
84
84
  },
85
85
  "devDependencies": {
@@ -104,8 +104,8 @@
104
104
  "ai": "6.0.0-beta.138",
105
105
  "fastify": "^4.23.2",
106
106
  "knex": "^3.1.0",
107
- "typescript": "^5.9.3",
108
107
  "pgvector": "^0.2.1",
108
+ "typescript": "^5.9.3",
109
109
  "voyageai": "^0.0.8"
110
110
  },
111
111
  "peerDependenciesMeta": {
@@ -130,7 +130,9 @@
130
130
  },
131
131
  "scripts": {
132
132
  "dev": "nodemon exec",
133
- "build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
133
+ "build": "run-s 'build:sonamu' 'build:ui-web'",
134
+ "build:sonamu": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
135
+ "build:ui-web": "cd ui-web && pnpm install && pnpm build",
134
136
  "test:type": "vitest --typecheck test-d.ts"
135
137
  }
136
138
  }
package/src/api/config.ts CHANGED
@@ -30,9 +30,6 @@ export type SonamuConfig = {
30
30
  sync: {
31
31
  targets: string[]; // "web", "app" 등
32
32
  };
33
- ui?: {
34
- port: number;
35
- };
36
33
 
37
34
  database: {
38
35
  // 데이터베이스
package/src/api/sonamu.ts CHANGED
@@ -231,8 +231,6 @@ class SonamuClass {
231
231
  await this.syncer.sync();
232
232
 
233
233
  await this.startWatcher();
234
-
235
- this.syncer.syncUI();
236
234
  }
237
235
 
238
236
  this.isInitialized = true;
@@ -346,10 +344,19 @@ class SonamuClass {
346
344
  },
347
345
  );
348
346
 
347
+ // Sonamu UI API
348
+ const { sonamuUIApiPlugin } = await import("../ui/api");
349
+ server.register(sonamuUIApiPlugin);
350
+
349
351
  // API 라우팅 (로컬HMR 상태와 구분)
350
352
  const { isLocal } = await import("../utils/controller");
351
353
  if (isLocal()) {
352
354
  server.all("*", async (request, reply) => {
355
+ // Sonamu UI
356
+ if (request.url.startsWith("/sonamu-ui")) {
357
+ return;
358
+ }
359
+
353
360
  const found = this.syncer.apis.find(
354
361
  (api) =>
355
362
  this.config.api.route.prefix + api.path === request.url.split("?")[0] &&
@@ -358,8 +365,14 @@ class SonamuClass {
358
365
  if (found) {
359
366
  return this.getApiHandler(found, config)(request, reply);
360
367
  }
361
- const { NotFoundException } = await import("../exceptions/so-exceptions");
362
- throw new NotFoundException("존재하지 않는 API 접근입니다.");
368
+
369
+ if (request.url.startsWith("/api/")) {
370
+ const { NotFoundException } = await import("../exceptions/so-exceptions");
371
+ throw new NotFoundException(`존재하지 않는 API 접근입니다. ${request.url}`);
372
+ }
373
+
374
+ // 일반 파일 접근시 별도의 에러 출력하지 않음
375
+ return;
363
376
  });
364
377
  } else {
365
378
  for (const api of this.syncer.apis) {
package/src/bin/cli.ts CHANGED
@@ -22,7 +22,7 @@ import { BUILD_DIR, SWC_BUILD_COMMAND, TSC_TYPE_CHECK_COMMAND } from "./build-co
22
22
  let migrator: Migrator;
23
23
 
24
24
  async function bootstrap() {
25
- const notToInit = ["dev", "build", "start", "ui"].includes(process.argv[2] ?? "");
25
+ const notToInit = ["dev", "build", "start"].includes(process.argv[2] ?? "");
26
26
  if (!notToInit) {
27
27
  await Sonamu.init(false, false);
28
28
  }
@@ -58,7 +58,6 @@ async function bootstrap() {
58
58
  ["scaffold", "model_test", "#entityId"],
59
59
  ["scaffold", "view_list", "#entityId"],
60
60
  ["scaffold", "view_form", "#entityId"],
61
- ["ui"],
62
61
  ["sync"],
63
62
  ["dev"],
64
63
  ["build"],
@@ -74,7 +73,6 @@ async function bootstrap() {
74
73
  stub_entity,
75
74
  scaffold_model,
76
75
  scaffold_model_test,
77
- ui,
78
76
  // scaffold_view_list,
79
77
  // scaffold_view_form,
80
78
  sync,
@@ -452,67 +450,3 @@ async function scaffold_model_test(entityId: string) {
452
450
  entityId,
453
451
  });
454
452
  }
455
-
456
- async function ui() {
457
- try {
458
- const apiRootPath = findApiRootPath();
459
-
460
- // 사용자 프로젝트의 패키지들 중에서 @sonamu-kit/ui를 찾습니다.
461
- // 이를 위해서 createRequire를 사용하여 프로젝트 경로 기준으로 resolve합니다.
462
- const projectRequire = createRequire(path.join(apiRootPath, "package.json"));
463
- const uiPackagePath = projectRequire.resolve("@sonamu-kit/ui"); // 없으면 여기서 터져요(MODULE_NOT_FOUND)
464
- const uiNodePath = path.join(path.dirname(uiPackagePath), "run-ui.js");
465
-
466
- if (!(await exists(uiNodePath))) {
467
- console.log(
468
- chalk.red(`UI runner script not found at ${uiNodePath}. Please rebuild @sonamu-kit/ui.`),
469
- );
470
- return;
471
- }
472
-
473
- // UI를 별도 프로세스로 실행 (hmr-hook 활성화)
474
- const uiProcess = spawn(
475
- process.execPath,
476
- [
477
- "--import",
478
- "sonamu/ts-loader-register",
479
- "--import",
480
- "sonamu/hmr-hook-register",
481
- "--enable-source-maps",
482
- "--no-warnings",
483
- uiNodePath,
484
- ],
485
- {
486
- stdio: "inherit",
487
- env: {
488
- ...process.env,
489
- HOT: "yes",
490
- API_ROOT_PATH: apiRootPath, // UI는 얘만 알면 돼요! 나머지는 얘가 떠서 알아서 할 것임 ㅎ
491
- },
492
- },
493
- );
494
-
495
- // 종료 처리
496
- const cleanup = () => {
497
- console.log(chalk.yellow("\n\n👋 Shutting down UI server..."));
498
- uiProcess.kill("SIGTERM");
499
- process.exit(0);
500
- };
501
-
502
- process.on("SIGINT", cleanup);
503
- process.on("SIGTERM", cleanup);
504
-
505
- uiProcess.on("exit", (code) => {
506
- if (code !== 0) {
507
- console.error(chalk.red(`❌ UI server exited with code ${code}`));
508
- process.exit(code || 1);
509
- }
510
- });
511
- } catch (e: unknown) {
512
- if (e instanceof Error && e.message.includes("isn't declared")) {
513
- console.log(`You need to install ${chalk.blue(`@sonamu-kit/ui`)} first.`);
514
- return;
515
- }
516
- throw e;
517
- }
518
- }
@@ -98,7 +98,8 @@ export async function readApisFromFile(filePath: AbsolutePath): Promise<Extended
98
98
  // const p = path.join(tmpdir(), "sonamu-syncer-error.json");
99
99
  // writeFileSync(p, JSON.stringify(registeredApis, null, 2));
100
100
  // execSync(`open ${p}`);
101
- throw new Error(`현재 파일에 사전 등록된 API가 없습니다. ${filePath}`);
101
+ // throw new Error(`현재 파일에 사전 등록된 API가 없습니다. ${filePath}`);
102
+ return [];
102
103
  }
103
104
 
104
105
  // 등록된 API에 현재 메소드 타입 정보 확장
@@ -1,6 +1,7 @@
1
1
  import { hot } from "@sonamu-kit/hmr-hook";
2
2
  import assert from "assert";
3
3
  import chalk from "chalk";
4
+ import { EventEmitter } from "events";
4
5
  import { mkdir, readFile, writeFile } from "fs/promises";
5
6
  import inflection from "inflection";
6
7
  import { minimatch } from "minimatch";
@@ -45,6 +46,7 @@ export class Syncer {
45
46
  models: LoadedModels = {};
46
47
  workflows: Map<string, WorkflowMetadata[]> = new Map();
47
48
  isSyncing: boolean = false;
49
+ eventEmitter: EventEmitter = new EventEmitter();
48
50
 
49
51
  /**
50
52
  * 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
@@ -100,17 +102,24 @@ export class Syncer {
100
102
  console.log(chalk.bold(`🔄 Invalidated:`));
101
103
 
102
104
  for (const invalidatedPath of invalidatedPaths) {
103
- // 만약 model.ts 파일이 변경(invalidate)되었다? 그러면 registeredApis 중에서 이 모델에 해당하는 api들은 지워줘요.
104
- // registeredApis는 통으로 날려버릴 없습니다. registeredApis 올라오는 친구들은 초기 로드시 또는 HMR시에만 등록되기 때문입니다.
105
- // 따라서 model.ts 파일의 변경으로 다음번 새로운 eval이 예상되는 시점에서만, 모델에서 나온 registeredApis들을 지워줄 수 있습니다.
106
- const removedApis = this.removeInvalidatedRegisteredApis(invalidatedPath);
107
- if (removedApis.length > 0) {
108
- console.log(
109
- chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`),
110
- chalk.gray(`(with ${removedApis.length} APIs)`),
105
+ try {
106
+ // 만약 model.ts 파일이 변경(invalidate)되었다? 그러면 registeredApis 중에서 모델에 해당하는 api들은 지워줘요.
107
+ // registeredApis는 통으로 날려버릴 없습니다. registeredApis에 올라오는 친구들은 초기 로드시 또는 HMR시에만 등록되기 때문입니다.
108
+ // 따라서 model.ts 파일의 변경으로 다음번 새로운 eval이 예상되는 이 시점에서만, 이 모델에서 나온 registeredApis들을 지워줄 수 있습니다.
109
+ const removedApis = this.removeInvalidatedRegisteredApis(invalidatedPath);
110
+ if (removedApis.length > 0) {
111
+ console.log(
112
+ chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`),
113
+ chalk.gray(`(with ${removedApis.length} APIs)`),
114
+ );
115
+ } else {
116
+ console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`));
117
+ }
118
+ } catch (e) {
119
+ console.error(e);
120
+ console.error(
121
+ chalk.red(`Failed to remove invalidated registered APIs for ${invalidatedPath}`),
111
122
  );
112
- } else {
113
- console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`));
114
123
  }
115
124
  }
116
125
  }
@@ -132,7 +141,7 @@ export class Syncer {
132
141
  await this.autoloadApis();
133
142
  await this.autoloadWorkflows();
134
143
 
135
- this.syncUI();
144
+ this.eventEmitter.emit("onHMRCompleted");
136
145
  }
137
146
 
138
147
  removeInvalidatedRegisteredApis(
@@ -551,16 +560,6 @@ export class Syncer {
551
560
  );
552
561
  }
553
562
 
554
- syncUI() {
555
- const uiPort = Sonamu.config.ui?.port ?? 57000;
556
-
557
- if (!isTest()) {
558
- fetch(`http://127.0.0.1:${uiPort}/api/reload`, {
559
- method: "GET",
560
- }).catch((e) => console.log(chalk.dim(`Failed to reload Sonamu UI: ${e.message}`)));
561
- }
562
- }
563
-
564
563
  /**
565
564
  * 하위호환용 프록시 메소드입니다.
566
565
  */
@@ -0,0 +1,60 @@
1
+ // import { convertToModelMessages, type UIMessage } from "ai";
2
+ // import type { FastifyInstance } from "fastify";
3
+ // import { BadRequestException, type FixtureRecord } from "sonamu";
4
+ // import { aiClient } from "./ai-client";
5
+
6
+ // export async function setAiApi(server: FastifyInstance) {
7
+ // await aiClient.init();
8
+
9
+ // server.post("/api/ai/fixture/chat", async (request, reply) => {
10
+ // const { messages, fixtureRecords } = request.body as {
11
+ // messages: UIMessage[];
12
+ // fixtureRecords?: FixtureRecord[];
13
+ // };
14
+
15
+ // if (!fixtureRecords || fixtureRecords.length === 0) {
16
+ // throw new BadRequestException("픽스쳐 레코드가 없습니다. 픽스쳐 조회 후 시도하세요.");
17
+ // }
18
+
19
+ // const result = aiClient.handleFixture(convertToModelMessages(messages), fixtureRecords);
20
+ // const response = result.toUIMessageStreamResponse();
21
+
22
+ // reply.raw.writeHead(response.status, Object.fromEntries(response.headers.entries()));
23
+
24
+ // if (response.body) {
25
+ // const reader = response.body.getReader();
26
+ // while (true) {
27
+ // const { done, value } = await reader.read();
28
+ // if (done) break;
29
+ // reply.raw.write(value);
30
+ // }
31
+ // }
32
+
33
+ // reply.raw.end();
34
+ // return reply;
35
+ // });
36
+
37
+ // // Entity/Enum 생성용 AI Chat Stream
38
+ // server.post("/api/ai/entity/chat", async (request, reply) => {
39
+ // const { messages } = request.body as {
40
+ // messages: UIMessage[];
41
+ // };
42
+
43
+ // const result = aiClient.handleEntity(convertToModelMessages(messages));
44
+ // const response = result.toUIMessageStreamResponse();
45
+
46
+ // reply.raw.writeHead(response.status, Object.fromEntries(response.headers.entries()));
47
+
48
+ // if (response.body) {
49
+ // const reader = response.body.getReader();
50
+ // while (true) {
51
+ // const { done, value } = await reader.read();
52
+ // if (done) break;
53
+ // reply.raw.write(value);
54
+ // }
55
+ // }
56
+
57
+ // reply.raw.end();
58
+ // return reply;
59
+ // });
60
+ // }