work-agent 0.1.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 (245) hide show
  1. package/README.md +234 -0
  2. package/app/(admin)/approvals/page.tsx +16 -0
  3. package/app/(admin)/audit/page.tsx +18 -0
  4. package/app/(admin)/layout.tsx +47 -0
  5. package/app/(admin)/scheduled-tasks/page.tsx +17 -0
  6. package/app/(admin)/settings/page.tsx +46 -0
  7. package/app/(admin)/skills/[name]/page.tsx +378 -0
  8. package/app/(admin)/skills/page.tsx +406 -0
  9. package/app/(admin)/statistics/page.tsx +416 -0
  10. package/app/(admin)/tickets/[id]/page.tsx +348 -0
  11. package/app/(admin)/tickets/new/page.tsx +309 -0
  12. package/app/(admin)/tickets/page.tsx +27 -0
  13. package/app/api/audit/route.ts +30 -0
  14. package/app/api/auth/feishu/callback/route.ts +72 -0
  15. package/app/api/auth/feishu/login/route.ts +17 -0
  16. package/app/api/auth/feishu/sso/route.ts +78 -0
  17. package/app/api/auth/login/route.ts +85 -0
  18. package/app/api/auth/oauth/route.ts +168 -0
  19. package/app/api/config/providers/route.ts +105 -0
  20. package/app/api/config/route.ts +115 -0
  21. package/app/api/config/status/route.ts +56 -0
  22. package/app/api/config/test/route.ts +212 -0
  23. package/app/api/documents/[id]/route.ts +88 -0
  24. package/app/api/documents/route.ts +53 -0
  25. package/app/api/health/route.ts +32 -0
  26. package/app/api/knowledge/[id]/route.ts +152 -0
  27. package/app/api/knowledge/from-session/route.ts +27 -0
  28. package/app/api/knowledge/route.ts +100 -0
  29. package/app/api/market/knowledge/[id]/route.ts +92 -0
  30. package/app/api/market/knowledge/route.ts +130 -0
  31. package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
  32. package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
  33. package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
  34. package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
  35. package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
  36. package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
  37. package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
  38. package/app/api/marketplace/skills/[id]/route.ts +177 -0
  39. package/app/api/marketplace/skills/route.ts +235 -0
  40. package/app/api/memory/route.ts +40 -0
  41. package/app/api/my/files/[id]/route.ts +52 -0
  42. package/app/api/my/files/route.ts +230 -0
  43. package/app/api/my/knowledge/route.ts +36 -0
  44. package/app/api/pi-chat/route.ts +443 -0
  45. package/app/api/recommend/route.ts +38 -0
  46. package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
  47. package/app/api/scheduled-tasks/[id]/route.ts +165 -0
  48. package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
  49. package/app/api/scheduled-tasks/route.ts +101 -0
  50. package/app/api/sessions/[id]/messages/route.ts +212 -0
  51. package/app/api/sessions/route.ts +101 -0
  52. package/app/api/share/file/[id]/route.ts +37 -0
  53. package/app/api/skills/[name]/execute/route.ts +121 -0
  54. package/app/api/skills/[name]/route.ts +167 -0
  55. package/app/api/skills/create/route.ts +65 -0
  56. package/app/api/skills/generate/route.ts +405 -0
  57. package/app/api/skills/installed/route.ts +151 -0
  58. package/app/api/skills/route.ts +174 -0
  59. package/app/api/skills/translate/route.ts +40 -0
  60. package/app/api/skills/user/[name]/route.ts +159 -0
  61. package/app/api/skills/user/route.ts +90 -0
  62. package/app/api/statistics/route.ts +94 -0
  63. package/app/api/task-executions/[id]/route.ts +34 -0
  64. package/app/api/task-executions/route.ts +29 -0
  65. package/app/api/tickets/[id]/approve/route.ts +129 -0
  66. package/app/api/tickets/[id]/execute/route.ts +201 -0
  67. package/app/api/tickets/[id]/route.ts +127 -0
  68. package/app/api/tickets/route.ts +103 -0
  69. package/app/api/user/skills/route.ts +175 -0
  70. package/app/api/users/route.ts +80 -0
  71. package/app/chat/page.tsx +5 -0
  72. package/app/globals.css +84 -0
  73. package/app/h5/layout.tsx +5 -0
  74. package/app/h5/mobile-approvals-page.tsx +167 -0
  75. package/app/h5/mobile-chat-page.tsx +951 -0
  76. package/app/h5/mobile-profile-page.tsx +147 -0
  77. package/app/h5/mobile-tickets-page.tsx +121 -0
  78. package/app/h5/page.tsx +23 -0
  79. package/app/h5/ticket-action-buttons.tsx +80 -0
  80. package/app/layout.tsx +26 -0
  81. package/app/login/page.tsx +318 -0
  82. package/app/market/knowledge/[id]/page.tsx +77 -0
  83. package/app/market/knowledge/page.tsx +358 -0
  84. package/app/market/layout.tsx +29 -0
  85. package/app/market/page.tsx +18 -0
  86. package/app/market/skills/page.tsx +397 -0
  87. package/app/my/files/page.tsx +511 -0
  88. package/app/my/knowledge/[id]/page.tsx +271 -0
  89. package/app/my/knowledge/new/page.tsx +234 -0
  90. package/app/my/knowledge/page.tsx +248 -0
  91. package/app/my/layout.tsx +32 -0
  92. package/app/my/memory/page.tsx +164 -0
  93. package/app/my/page.tsx +18 -0
  94. package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
  95. package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
  96. package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
  97. package/app/my/scheduled-tasks/new/page.tsx +230 -0
  98. package/app/my/scheduled-tasks/page.tsx +27 -0
  99. package/app/my/skills/[name]/page.tsx +320 -0
  100. package/app/my/skills/new/page.tsx +394 -0
  101. package/app/my/skills/page.tsx +303 -0
  102. package/app/page.tsx +2288 -0
  103. package/app/share/[sessionId]/page.tsx +226 -0
  104. package/app/share/file/[id]/page.tsx +140 -0
  105. package/bin/README.md +63 -0
  106. package/bin/generate-api-system +300 -0
  107. package/bin/postinstall.js +95 -0
  108. package/bin/work-agent.js +173 -0
  109. package/components/ai-elements/agent.tsx +142 -0
  110. package/components/ai-elements/artifact.tsx +149 -0
  111. package/components/ai-elements/attachments.tsx +427 -0
  112. package/components/ai-elements/audio-player.tsx +232 -0
  113. package/components/ai-elements/canvas.tsx +26 -0
  114. package/components/ai-elements/chain-of-thought.tsx +223 -0
  115. package/components/ai-elements/checkpoint.tsx +72 -0
  116. package/components/ai-elements/code-block.tsx +555 -0
  117. package/components/ai-elements/commit.tsx +449 -0
  118. package/components/ai-elements/confirmation.tsx +173 -0
  119. package/components/ai-elements/connection.tsx +28 -0
  120. package/components/ai-elements/context.tsx +410 -0
  121. package/components/ai-elements/controls.tsx +19 -0
  122. package/components/ai-elements/conversation.tsx +167 -0
  123. package/components/ai-elements/edge.tsx +144 -0
  124. package/components/ai-elements/environment-variables.tsx +325 -0
  125. package/components/ai-elements/file-tree.tsx +298 -0
  126. package/components/ai-elements/image.tsx +25 -0
  127. package/components/ai-elements/inline-citation.tsx +294 -0
  128. package/components/ai-elements/jsx-preview.tsx +250 -0
  129. package/components/ai-elements/message.tsx +367 -0
  130. package/components/ai-elements/mic-selector.tsx +372 -0
  131. package/components/ai-elements/model-selector.tsx +214 -0
  132. package/components/ai-elements/node.tsx +72 -0
  133. package/components/ai-elements/open-in-chat.tsx +367 -0
  134. package/components/ai-elements/package-info.tsx +235 -0
  135. package/components/ai-elements/panel.tsx +16 -0
  136. package/components/ai-elements/persona.tsx +280 -0
  137. package/components/ai-elements/plan.tsx +144 -0
  138. package/components/ai-elements/prompt-input.tsx +1341 -0
  139. package/components/ai-elements/queue.tsx +275 -0
  140. package/components/ai-elements/reasoning.tsx +355 -0
  141. package/components/ai-elements/sandbox.tsx +133 -0
  142. package/components/ai-elements/schema-display.tsx +473 -0
  143. package/components/ai-elements/shimmer.tsx +78 -0
  144. package/components/ai-elements/snippet.tsx +141 -0
  145. package/components/ai-elements/sources.tsx +78 -0
  146. package/components/ai-elements/speech-input.tsx +324 -0
  147. package/components/ai-elements/stack-trace.tsx +531 -0
  148. package/components/ai-elements/suggestion.tsx +58 -0
  149. package/components/ai-elements/task.tsx +88 -0
  150. package/components/ai-elements/terminal.tsx +277 -0
  151. package/components/ai-elements/test-results.tsx +497 -0
  152. package/components/ai-elements/tool.tsx +174 -0
  153. package/components/ai-elements/toolbar.tsx +17 -0
  154. package/components/ai-elements/transcription.tsx +126 -0
  155. package/components/ai-elements/voice-selector.tsx +525 -0
  156. package/components/ai-elements/web-preview.tsx +282 -0
  157. package/components/audit-log-list.tsx +114 -0
  158. package/components/chat/EmptyPreviewState.tsx +12 -0
  159. package/components/chat/KnowledgePickerDialog.tsx +464 -0
  160. package/components/chat/KnowledgePreview.tsx +70 -0
  161. package/components/chat/KnowledgePreviewPanel.tsx +86 -0
  162. package/components/chat/MentionInput.tsx +309 -0
  163. package/components/chat/OrganizeDialog.tsx +258 -0
  164. package/components/chat/RecommendationBanner.tsx +94 -0
  165. package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
  166. package/components/chat/SkillSelector.tsx +305 -0
  167. package/components/chat/SkillSwitcher.tsx +163 -0
  168. package/components/client-layout.tsx +15 -0
  169. package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
  170. package/components/layout-wrapper.tsx +18 -0
  171. package/components/mobile-layout.tsx +62 -0
  172. package/components/scheduled-task-list.tsx +356 -0
  173. package/components/setup-guide.tsx +484 -0
  174. package/components/sub-nav.tsx +54 -0
  175. package/components/ticket-detail-content.tsx +383 -0
  176. package/components/ticket-list.tsx +366 -0
  177. package/components/top-nav.tsx +132 -0
  178. package/components/ui/accordion.tsx +58 -0
  179. package/components/ui/alert.tsx +59 -0
  180. package/components/ui/avatar.tsx +50 -0
  181. package/components/ui/badge.tsx +36 -0
  182. package/components/ui/button-group.tsx +83 -0
  183. package/components/ui/button.tsx +57 -0
  184. package/components/ui/card.tsx +91 -0
  185. package/components/ui/carousel.tsx +262 -0
  186. package/components/ui/collapsible.tsx +11 -0
  187. package/components/ui/command.tsx +153 -0
  188. package/components/ui/dialog.tsx +122 -0
  189. package/components/ui/dropdown-menu.tsx +200 -0
  190. package/components/ui/hover-card.tsx +29 -0
  191. package/components/ui/input-group.tsx +170 -0
  192. package/components/ui/input.tsx +22 -0
  193. package/components/ui/label.tsx +26 -0
  194. package/components/ui/popover.tsx +31 -0
  195. package/components/ui/progress.tsx +28 -0
  196. package/components/ui/scroll-area.tsx +48 -0
  197. package/components/ui/select.tsx +174 -0
  198. package/components/ui/separator.tsx +31 -0
  199. package/components/ui/spinner.tsx +16 -0
  200. package/components/ui/switch.tsx +29 -0
  201. package/components/ui/table.tsx +120 -0
  202. package/components/ui/tabs.tsx +55 -0
  203. package/components/ui/textarea.tsx +22 -0
  204. package/components/ui/tooltip.tsx +30 -0
  205. package/components/welcome-guide.tsx +182 -0
  206. package/components.json +24 -0
  207. package/lib/command-parser.ts +331 -0
  208. package/lib/dangerous-commands.ts +672 -0
  209. package/lib/db.ts +2250 -0
  210. package/lib/feishu-auth.ts +135 -0
  211. package/lib/file-storage.ts +306 -0
  212. package/lib/file-tool.ts +583 -0
  213. package/lib/knowledge-tool.ts +152 -0
  214. package/lib/knowledge-types.ts +66 -0
  215. package/lib/market-client.ts +313 -0
  216. package/lib/market-db.ts +736 -0
  217. package/lib/market-types.ts +51 -0
  218. package/lib/memory-tool.ts +211 -0
  219. package/lib/memory.ts +197 -0
  220. package/lib/pi-config.ts +436 -0
  221. package/lib/pi-session.ts +799 -0
  222. package/lib/pinyin.ts +13 -0
  223. package/lib/recommendation.ts +227 -0
  224. package/lib/risk-estimator.ts +350 -0
  225. package/lib/scheduled-task-tool.ts +184 -0
  226. package/lib/scheduler-init.ts +43 -0
  227. package/lib/scheduler.ts +416 -0
  228. package/lib/secure-bash-tool.ts +413 -0
  229. package/lib/skill-engine.ts +396 -0
  230. package/lib/skill-generator.ts +269 -0
  231. package/lib/skill-loader.ts +234 -0
  232. package/lib/skill-tool.ts +188 -0
  233. package/lib/skill-types.ts +82 -0
  234. package/lib/skills-init.ts +58 -0
  235. package/lib/ticket-tool.ts +246 -0
  236. package/lib/user-skill-types.ts +30 -0
  237. package/lib/user-skills.ts +362 -0
  238. package/lib/utils.ts +6 -0
  239. package/lib/workflow.ts +154 -0
  240. package/lib/zip-tool.ts +191 -0
  241. package/next.config.js +8 -0
  242. package/package.json +106 -0
  243. package/public/.gitkeep +1 -0
  244. package/public/icon.svg +1 -0
  245. package/tsconfig.json +42 -0
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * API System Generator - CLI Tool
4
+ * ็”Ÿๆˆ Skill ๅˆฐ .pi/skills/ ็›ฎๅฝ•
5
+ */
6
+
7
+ const { writeFileSync, mkdirSync, chmodSync, existsSync, readFileSync } = require('fs');
8
+ const { join, dirname } = require('path');
9
+
10
+ const HELP = `
11
+ API Skill Generator - ไปŽ Swagger ็”Ÿๆˆ AI ๅฏ่ฏปๅ–็š„ Skill
12
+
13
+ ็”จๆณ•:
14
+ generate-api-system -s <swagger-url> [้€‰้กน]
15
+
16
+ ้€‰้กน:
17
+ -s, --swagger <url> Swagger/OpenAPI ๆ–‡ๆกฃๅœฐๅ€ (ๅฟ…ๅกซ)
18
+ -n, --name <name> Skill ๅ็งฐ (้ป˜่ฎค: ไปŽ Swagger ๆๅ–)
19
+ -d, --description <desc> Skill ๆ่ฟฐ
20
+ -b, --base-url <url> API ๅŸบ็ก€ๅœฐๅ€
21
+ -t, --token <token> API Token
22
+ -h, --help ๆ˜พ็คบๅธฎๅŠฉ
23
+
24
+ ็คบไพ‹:
25
+ # ๅŸบๆœฌ็”จๆณ•
26
+ generate-api-system -s "https://api.example.com/openapi.json"
27
+
28
+ # ๅฎŒๆ•ดๅ‚ๆ•ฐ
29
+ generate-api-system -s "https://api.example.com/openapi.json" \\
30
+ -n "my-api" \\
31
+ -d "ๆˆ‘็š„API็ณป็ปŸ" \\
32
+ -b "https://api.example.com"
33
+
34
+ ่พ“ๅ‡บ (็”Ÿๆˆๅˆฐ .pi/skills/):
35
+ .pi/skills/{name}/
36
+ โ”œโ”€โ”€ SKILL.md โ† AI Skill ๅฎšไน‰
37
+ โ”œโ”€โ”€ SKILL.json โ† ็ป“ๆž„ๅŒ–ๅ…ƒๆ•ฐๆฎ
38
+ โ”œโ”€โ”€ config.env โ† ็Žฏๅขƒๅ˜้‡้…็ฝฎ๏ผˆไพ›่„šๆœฌไฝฟ็”จ๏ผ‰
39
+ โ”œโ”€โ”€ config.yaml โ† API ้…็ฝฎ่ฏดๆ˜Ž
40
+ โ”œโ”€โ”€ scripts/ โ† ๅฏๆ‰ง่กŒ่„šๆœฌ
41
+ โ””โ”€โ”€ curl_examples/ โ† curl ็คบไพ‹
42
+ `;
43
+
44
+ function extractName(swaggerUrl, spec) {
45
+ if (spec.info?.title) {
46
+ return slugify(spec.info.title);
47
+ }
48
+ if (spec.info?.version) {
49
+ return slugify(spec.info.title || 'api') + '-' + slugify(spec.info.version);
50
+ }
51
+ try {
52
+ const url = new URL(swaggerUrl);
53
+ return slugify(url.hostname);
54
+ } catch {
55
+ return 'api-system';
56
+ }
57
+ }
58
+
59
+ function slugify(str) {
60
+ return str.toLowerCase()
61
+ .replace(/[^a-z0-9]+/g, '-')
62
+ .replace(/^-|-$/g, '');
63
+ }
64
+
65
+ function extractBaseUrl(swaggerUrl, spec) {
66
+ if (spec.servers?.[0]?.url) {
67
+ return spec.servers[0].url;
68
+ }
69
+ try {
70
+ const url = new URL(swaggerUrl);
71
+ return url.origin + url.pathname.replace(/\/[^/]*$/, '');
72
+ } catch {
73
+ return '';
74
+ }
75
+ }
76
+
77
+ async function generate() {
78
+ const args = process.argv.slice(2);
79
+
80
+ if (args.includes('-h') || args.includes('--help')) {
81
+ console.log(HELP);
82
+ process.exit(0);
83
+ }
84
+
85
+ const options = {
86
+ swaggerUrl: '',
87
+ name: '',
88
+ description: '',
89
+ baseUrl: ''
90
+ };
91
+
92
+ for (let i = 0; i < args.length; i++) {
93
+ const arg = args[i];
94
+ if (arg === '-s' || arg === '--swagger') options.swaggerUrl = args[++i];
95
+ if (arg === '-n' || arg === '--name') options.name = args[++i];
96
+ if (arg === '-d' || arg === '--description') options.description = args[++i];
97
+ if (arg === '-b' || arg === '--base-url') options.baseUrl = args[++i];
98
+ }
99
+
100
+ if (!options.swaggerUrl) {
101
+ console.error('้”™่ฏฏ: ่ฏทๆŒ‡ๅฎš Swagger URL (-s <url>)');
102
+ console.error('ๅธฎๅŠฉ: generate-api-system --help');
103
+ process.exit(1);
104
+ }
105
+
106
+ console.log('Fetching swagger...');
107
+ const response = await fetch(options.swaggerUrl);
108
+ if (!response.ok) {
109
+ console.error('Failed to fetch swagger:', response.statusText);
110
+ process.exit(1);
111
+ }
112
+ const spec = await response.json();
113
+
114
+ const skillName = options.name || extractName(options.swaggerUrl, spec);
115
+ const skillDescription = options.description || spec.info?.description || spec.info?.title || 'API System';
116
+ const baseUrl = options.baseUrl || extractBaseUrl(options.swaggerUrl, spec);
117
+
118
+ // ่พ“ๅ‡บๅˆฐ .pi/skills/
119
+ const piSkillsDir = '.pi/skills';
120
+ const skillDir = join(piSkillsDir, skillName);
121
+ const scriptsDir = join(skillDir, 'scripts');
122
+ const curlDir = join(skillDir, 'curl_examples');
123
+
124
+ mkdirSync(piSkillsDir, { recursive: true });
125
+ mkdirSync(skillDir, { recursive: true });
126
+ mkdirSync(scriptsDir, { recursive: true });
127
+ mkdirSync(curlDir, { recursive: true });
128
+
129
+ console.log('Generating skill:', skillName);
130
+ console.log('Output:', skillDir);
131
+
132
+ // ็”Ÿๆˆ config.yaml
133
+ writeFileSync(join(skillDir, 'config.yaml'), `# ${skillName} - API้…็ฝฎ
134
+
135
+ api:
136
+ baseUrl: ${baseUrl}
137
+ auth:
138
+ type: bearer
139
+ credentials:
140
+ token: $TOKEN
141
+ `);
142
+
143
+ // ็”Ÿๆˆ config.env (ไพ›่„šๆœฌไฝฟ็”จ)
144
+ writeFileSync(join(skillDir, 'config.env'), `# ${skillName} - ็Žฏๅขƒๅ˜้‡้…็ฝฎ
145
+ BASE_URL=${baseUrl}
146
+ TOKEN=your-token-here
147
+ `);
148
+
149
+ // ็”Ÿๆˆ SKILL.json
150
+ const paths = spec.paths || {};
151
+ const endpoints = [];
152
+ const readOnlyOps = [];
153
+ const modifyOps = [];
154
+
155
+ for (const [path, item] of Object.entries(paths)) {
156
+ const normalizedPath = path.replace(/\{/g, ':').replace(/\}/g, '');
157
+ for (const [method, op] of Object.entries(item)) {
158
+ if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
159
+ const operation = op;
160
+ const endpoint = {
161
+ method: method.toUpperCase(),
162
+ path,
163
+ summary: operation.summary || '',
164
+ isReadOnly: ['get', 'head', 'options'].includes(method)
165
+ };
166
+ endpoints.push(endpoint);
167
+ if (endpoint.isReadOnly) readOnlyOps.push(endpoint);
168
+ else modifyOps.push(endpoint);
169
+ }
170
+ }
171
+ }
172
+
173
+ const skillJson = {
174
+ name: skillName,
175
+ displayName: spec.info?.title || skillName,
176
+ description: skillDescription,
177
+ version: spec.info?.version || '1.0.0',
178
+ author: spec.info?.contact?.name || 'xuanwu',
179
+ keywords: ['api', 'generated', 'swagger'],
180
+ baseUrl,
181
+ endpoints: {
182
+ readOnly: readOnlyOps,
183
+ modify: modifyOps
184
+ },
185
+ scripts: {
186
+ readOnly: readOnlyOps.map(e => `${e.method.toLowerCase()}_${e.path.split('/').pop()?.replace(/[{}]/g, '')}`),
187
+ modify: modifyOps.map(e => `${e.method.toLowerCase()}_${e.path.split('/').pop()?.replace(/[{}]/g, '')}`)
188
+ }
189
+ };
190
+ writeFileSync(join(skillDir, 'SKILL.json'), JSON.stringify(skillJson, null, 2));
191
+
192
+ // ็”Ÿๆˆ SKILL.md
193
+ let skillMd = `# ${skillName}
194
+
195
+ ${skillDescription}
196
+
197
+ ## API ๅˆ—่กจ
198
+
199
+ ### ๅช่ฏปๆ“ไฝœ๏ผˆAI ่‡ชๅŠจๆ‰ง่กŒ๏ผ‰
200
+
201
+ | API | ๆ่ฟฐ | ่„šๆœฌ |
202
+ |-----|------|------|
203
+ ${readOnlyOps.map(e => `| \`${e.method} ${e.path}\` | ${e.summary || '-'} | \`scripts/${e.method.toLowerCase()}_${e.path.split('/').pop()?.replace(/[{}]/g, '')}\` |`).join('\n')}
204
+
205
+ ### ไฟฎๆ”นๆ“ไฝœ๏ผˆ้œ€ๅฎกๆ‰น๏ผ‰
206
+
207
+ | API | ๆ่ฟฐ | ่„šๆœฌ |
208
+ |-----|------|------|
209
+ ${modifyOps.map(e => `| \`${e.method} ${e.path}\` | ${e.summary || '-'} | \`scripts/${e.method.toLowerCase()}_${e.path.split('/').pop()?.replace(/[{}]/g, '')}\` |`).join('\n')}
210
+
211
+ ## ไฝฟ็”จๆ–นๅผ
212
+
213
+ ### ๅช่ฏปๆ“ไฝœ
214
+
215
+ \`\`\`bash
216
+ ./scripts/get_users
217
+ \`\`\`
218
+
219
+ ### ไฟฎๆ”นๆ“ไฝœ
220
+
221
+ \`\`\`bash
222
+ ./scripts/create_user # ๅˆ›ๅปบๅทฅๅ•๏ผŒ็ญ‰ๅพ…ๅฎกๆ‰น
223
+ \`\`\`
224
+
225
+ ## ้…็ฝฎ
226
+
227
+ ็ผ–่พ‘ \`config.env\` ้…็ฝฎ API ๅœฐๅ€ๅ’Œ่ฎค่ฏใ€‚
228
+
229
+ ## ็›ธๅ…ณๆ–‡ไปถ
230
+
231
+ - \`SKILL.json\` - ็ป“ๆž„ๅŒ–ๅ…ƒๆ•ฐๆฎ
232
+ - \`config.env\` - ็Žฏๅขƒๅ˜้‡้…็ฝฎ๏ผˆไพ›่„šๆœฌไฝฟ็”จ๏ผ‰
233
+ - \`config.yaml\` - API ้…็ฝฎ่ฏดๆ˜Ž
234
+ - \`scripts/\` - ๅฏๆ‰ง่กŒ่„šๆœฌ
235
+ `;
236
+ writeFileSync(join(skillDir, 'SKILL.md'), skillMd);
237
+
238
+ // ็”Ÿๆˆ่„šๆœฌ
239
+ for (const [path, item] of Object.entries(paths)) {
240
+ const normalizedPath = path.replace(/\{/g, ':').replace(/\}/g, '');
241
+ const groupName = path.split('/').filter(Boolean)[1] || 'default';
242
+
243
+ for (const [method, op] of Object.entries(item)) {
244
+ if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
245
+ const operation = op;
246
+ const summary = operation.summary || '';
247
+ const isReadOnly = ['get', 'head', 'options'].includes(method);
248
+ const scriptName = method.toLowerCase() + '_' + path.split('/').pop()?.replace(/[{}]/g, '')?.replace(/-/g, '_') || method;
249
+
250
+ const scriptContent = `#!/bin/bash
251
+ # ${scriptName} - ${summary}
252
+ # ๅˆ†็ฑป: ${isReadOnly ? 'ๅช่ฏปๆ“ไฝœ (read_only)' : 'ไฟฎๆ”นๆ“ไฝœ (modify)'}
253
+ # API: ${method.toUpperCase()} ${path}
254
+
255
+ SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
256
+ source "\${SCRIPT_DIR}/../config.env"
257
+
258
+ ${isReadOnly ? `# ๅช่ฏปๆ“ไฝœ - ็›ดๆŽฅๆ‰ง่กŒ
259
+ echo "ๆ‰ง่กŒ: ${method.toUpperCase()} ${path}"
260
+ curl -s -X ${method.toUpperCase()} "$BASE_URL${normalizedPath}" \\
261
+ -H "Authorization: Bearer $TOKEN" \\
262
+ -H "Content-Type: application/json" | jq .` : `# ไฟฎๆ”นๆ“ไฝœ - ้œ€่ฆๅฎกๆ‰น
263
+ echo "ๆ‰ง่กŒ: ${method.toUpperCase()} ${path}"
264
+ curl -s -X ${method.toUpperCase()} "$BASE_URL${normalizedPath}" \\
265
+ -H "Authorization: Bearer $TOKEN" \\
266
+ -H "Content-Type: application/json" \\
267
+ -d '{"data": "่ฏทไฟฎๆ”น"}' | jq .`}
268
+ `;
269
+ writeFileSync(join(scriptsDir, scriptName), scriptContent);
270
+ chmodSync(join(scriptsDir, scriptName), 0o755);
271
+
272
+ // curl ็คบไพ‹
273
+ const curlContent = `curl -X ${method.toUpperCase()} "${normalizedPath}" \\
274
+ -H "Authorization: Bearer $TOKEN" \\
275
+ -H "Content-Type: application/json"
276
+ `;
277
+ writeFileSync(join(curlDir, `${scriptName}.sh`), curlContent);
278
+ }
279
+ }
280
+ }
281
+
282
+ console.log('');
283
+ console.log('โœ… Skill ็”ŸๆˆๅฎŒๆˆ๏ผ');
284
+ console.log('๐Ÿ“ ่ทฏๅพ„:', skillDir);
285
+ console.log('');
286
+ console.log('ๆ–‡ไปถๅˆ—่กจ:');
287
+ console.log(' โ”œโ”€โ”€ SKILL.md โ† AI Skill ๅฎšไน‰');
288
+ console.log(' โ”œโ”€โ”€ SKILL.json โ† ็ป“ๆž„ๅŒ–ๅ…ƒๆ•ฐๆฎ');
289
+ console.log(' โ”œโ”€โ”€ config.env โ† ็Žฏๅขƒๅ˜้‡้…็ฝฎ๏ผˆไฟฎๆ”นๆญคๆ–‡ไปถ๏ผ‰');
290
+ console.log(' โ”œโ”€โ”€ config.yaml โ† API ้…็ฝฎ่ฏดๆ˜Ž');
291
+ console.log(' โ”œโ”€โ”€ scripts/ โ† ๅฏๆ‰ง่กŒ่„šๆœฌ');
292
+ console.log(' โ””โ”€โ”€ curl_examples/ โ† curl ็คบไพ‹');
293
+ console.log('');
294
+ console.log('AI ๅฏ็›ดๆŽฅ่ฏปๅ– .pi/skills/' + skillName + '/SKILL.md');
295
+ }
296
+
297
+ generate().catch(err => {
298
+ console.error('Error:', err);
299
+ process.exit(1);
300
+ });
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ /**
6
+ * postinstall.js
7
+ *
8
+ * npm install / npm install -g ๅฎŒๆˆๅŽ่‡ชๅŠจ่งฆๅ‘๏ผš
9
+ * 1. ๆฃ€ๆŸฅ Node.js ็‰ˆๆœฌ
10
+ * 2. ๅฆ‚ๆžœ .next/standalone ไธๅญ˜ๅœจ โ†’ ๆ‰ง่กŒ next build๏ผˆ้ฆ–ๆฌกๅฎ‰่ฃ…๏ผ‰
11
+ * 3. ๆ— ่ฎบๅฆ‚ไฝ• โ†’ ๅฏน .next/standalone ้‡Œ็š„ better-sqlite3 ๆ‰ง่กŒ rebuild
12
+ * ๏ผˆ็กฎไฟๅŽŸ็”Ÿๆจกๅ—ไธŽๅฝ“ๅ‰ Node.js ABI ๅŒน้…๏ผ‰
13
+ */
14
+
15
+ const { spawnSync } = require('child_process');
16
+ const path = require('path');
17
+ const fs = require('fs');
18
+
19
+ const PKG_ROOT = path.resolve(__dirname, '..');
20
+ const STANDALONE_DIR = path.join(PKG_ROOT, '.next', 'standalone');
21
+ const STANDALONE_SERVER = path.join(STANDALONE_DIR, 'server.js');
22
+ const BSQL3_NODE = path.join(
23
+ STANDALONE_DIR,
24
+ 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node'
25
+ );
26
+
27
+ // โ”€โ”€ CI ็Žฏๅขƒ โ†’ ๅ…จ้ƒจ่ทณ่ฟ‡ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
28
+ if (process.env.CI) {
29
+ console.log('[work-agent] CI environment detected, skipping postinstall.');
30
+ process.exit(0);
31
+ }
32
+
33
+ // โ”€โ”€ Node.js ็‰ˆๆœฌๆฃ€ๆŸฅ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
34
+ const [major] = process.versions.node.split('.').map(Number);
35
+ if (major < 18) {
36
+ console.error(`[work-agent] Node.js >= 18 is required. Current: v${process.versions.node}`);
37
+ process.exit(1);
38
+ }
39
+
40
+ // โ”€โ”€ Step 1: ๅฆ‚ๆžœๆฒกๆœ‰ standalone โ†’ ๅ…ˆ build โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
41
+ if (!fs.existsSync(STANDALONE_SERVER)) {
42
+ console.log('\n[work-agent] Building the app (first install, this may take a minute)...\n');
43
+
44
+ const buildResult = spawnSync('npm', ['run', 'build'], {
45
+ cwd: PKG_ROOT,
46
+ stdio: 'inherit',
47
+ shell: process.platform === 'win32',
48
+ });
49
+
50
+ if (buildResult.status !== 0) {
51
+ console.error('\n[work-agent] Build failed. Retry manually:\n');
52
+ console.error(` cd ${PKG_ROOT} && npm run build\n`);
53
+ process.exit(0);
54
+ }
55
+
56
+ console.log('\n[work-agent] Build complete.\n');
57
+ } else {
58
+ console.log('[work-agent] Standalone build found, skipping next build.');
59
+ }
60
+
61
+ // โ”€โ”€ Step 2: ็กฎไฟ better-sqlite3 ไธŽๅฝ“ๅ‰ Node.js ABI ๅŒน้… โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
62
+ // next build ไผšๆŠŠ better-sqlite3 ็š„ .node ๆ–‡ไปถๅคๅˆถ่ฟ› standalone๏ผŒ
63
+ // ไฝ†้‚ฃไธชๆ–‡ไปถๆ˜ฏๅฎ‰่ฃ…ๆ—ถ็ผ–่ฏ‘็š„๏ผŒๆขไบ† Node.js ็‰ˆๆœฌๅฐฑไผšๅดฉใ€‚
64
+ // ๆฏๆฌก postinstall ้ƒฝๅผบๅˆถ rebuild ไธ€ๆฌก๏ผŒไปฃไปทๅพˆไฝŽ๏ผˆ< 10 ็ง’๏ผ‰ใ€‚
65
+ if (fs.existsSync(BSQL3_NODE)) {
66
+ console.log('[work-agent] Rebuilding better-sqlite3 for current Node.js version...');
67
+
68
+ const rebuildResult = spawnSync(
69
+ 'npm',
70
+ ['rebuild', 'better-sqlite3'],
71
+ {
72
+ cwd: STANDALONE_DIR,
73
+ stdio: 'inherit',
74
+ shell: process.platform === 'win32',
75
+ env: {
76
+ ...process.env,
77
+ // ้˜ฒๆญข npm rebuild ่งฆๅ‘ๅ…ถไป–ๅŒ…็š„ postinstall
78
+ npm_lifecycle_event: 'rebuild',
79
+ },
80
+ }
81
+ );
82
+
83
+ if (rebuildResult.status !== 0) {
84
+ console.warn('[work-agent] better-sqlite3 rebuild failed. You may need build tools:');
85
+ console.warn(' macOS: xcode-select --install');
86
+ console.warn(' Linux: apt install -y python3 make g++ (or yum equivalent)');
87
+ console.warn(' Windows: npm install -g windows-build-tools');
88
+ } else {
89
+ console.log('[work-agent] better-sqlite3 rebuild complete.');
90
+ }
91
+ } else {
92
+ console.log('[work-agent] better-sqlite3 not found in standalone, skipping rebuild.');
93
+ }
94
+
95
+ console.log('\n[work-agent] Ready. Run `work-agent` to start.\n');
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { spawn, spawnSync } = require('child_process');
8
+ const http = require('http');
9
+
10
+ // โ”€โ”€ ่ทฏๅพ„่งฃๆž โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
+ const PKG_ROOT = path.resolve(__dirname, '..');
12
+ const STANDALONE_DIR = path.join(PKG_ROOT, '.next', 'standalone');
13
+ const STANDALONE_SERVER = path.join(STANDALONE_DIR, 'server.js');
14
+ const STATIC_SRC = path.join(PKG_ROOT, '.next', 'static');
15
+ const PUBLIC_SRC = path.join(PKG_ROOT, 'public');
16
+ const BSQL3_DIR = path.join(STANDALONE_DIR, 'node_modules', 'better-sqlite3');
17
+ const BSQL3_NODE = path.join(BSQL3_DIR, 'build', 'Release', 'better_sqlite3.node');
18
+
19
+ // โ”€โ”€ ็ซฏๅฃ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
+ const PORT = parseInt(process.env.PORT || '11024', 10);
21
+ const HOST = process.env.HOST || '0.0.0.0';
22
+
23
+ // โ”€โ”€ ๆฃ€ๆŸฅๆž„ๅปบไบง็‰ฉๆ˜ฏๅฆๅญ˜ๅœจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
24
+ if (!fs.existsSync(STANDALONE_SERVER)) {
25
+ console.error('\n[work-agent] ๆœชๆ‰พๅˆฐๆž„ๅปบไบง็‰ฉใ€‚่ฏทๅ…ˆ่ฟ่กŒ๏ผš\n');
26
+ console.error(' cd ' + PKG_ROOT);
27
+ console.error(' npm run build\n');
28
+ process.exit(1);
29
+ }
30
+
31
+ // โ”€โ”€ ๆฃ€ๆŸฅๅนถไฟฎๅค better-sqlite3 ABI ๅ…ผๅฎนๆ€ง โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
32
+ // .node ๆ–‡ไปถไธญๅตŒๅ…ฅไบ†็ผ–่ฏ‘ๆ—ถ็š„ NODE_MODULE_VERSION๏ผŒ้€š่ฟ‡่ฏปๅ–ๆ–‡ไปถๅ†…ๅฎนๆฅๆฃ€ๆต‹๏ผŒ
33
+ // ้ฟๅ… try/require ๅœจๆŸไบ›ๅนณๅฐไธŠ่กŒไธบไธไธ€่‡ดใ€‚
34
+ function getNodeAbiFromBinary(nodePath) {
35
+ try {
36
+ // NODE_MODULE_VERSION ไปฅๅญ—็ฌฆไธฒ "NODE_MODULE_VERSION_xx" ๆˆ–็›ดๆŽฅๆ•ฐๅญ—ๅตŒๅ…ฅๅœจไบŒ่ฟ›ๅˆถ้‡Œ
37
+ // ๆ›ดๅฏ้ ็š„ๆ–นๅผ๏ผš็›ดๆŽฅ require ๅนถๆ•่Žท้”™่ฏฏๆถˆๆฏ
38
+ const mod = require('module');
39
+ // ้€š่ฟ‡ process.versions.modules ่Žทๅ–ๅฝ“ๅ‰่ฟ่กŒๆ—ถ็š„ ABI
40
+ return parseInt(process.versions.modules, 10);
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ function needsRebuild() {
47
+ if (!fs.existsSync(BSQL3_NODE)) return false;
48
+
49
+ // ่ฏปๅ– .node ไบŒ่ฟ›ๅˆถ๏ผŒๅœจๅ…ถไธญๆœ็ดข "NODE_MODULE_VERSION" ๆ ‡่ฎฐๅŽ็š„็‰ˆๆœฌๅท
50
+ try {
51
+ const buf = fs.readFileSync(BSQL3_NODE);
52
+ const marker = Buffer.from('NODE_MODULE_VERSION');
53
+ let idx = buf.indexOf(marker);
54
+ if (idx === -1) {
55
+ // ๆ‰พไธๅˆฐๆ ‡่ฎฐ๏ผŒ็”จ try-require ๅ…œๅบ•
56
+ try {
57
+ require(path.join(BSQL3_DIR, 'lib', 'database.js'));
58
+ return false;
59
+ } catch {
60
+ return true;
61
+ }
62
+ }
63
+ // marker ไน‹ๅŽๆ˜ฏไธ€ไธชๅญ—่Š‚ 0x00๏ผˆๅˆ†้š”๏ผ‰๏ผŒ็„ถๅŽๆ˜ฏ็‰ˆๆœฌๅทๅญ—็ฌฆไธฒ๏ผˆๅฆ‚ "115\0"๏ผ‰
64
+ // ๅฎž้™…ๆ ผๅผๅ› ็ผ–่ฏ‘ๅทฅๅ…ท้“พไธๅŒ๏ผŒ็›ดๆŽฅ try-require ๆ›ดๅฏ้ 
65
+ return false; // fallback ๅˆฐ try-require
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ // ๆœ€ๅฏ้ ๆ–นๆกˆ๏ผš็›ดๆŽฅ try-require๏ผŒๆ•่Žท ERR_DLOPEN_FAILED
72
+ function checkAndRebuild() {
73
+ if (!fs.existsSync(BSQL3_NODE)) return;
74
+
75
+ let failed = false;
76
+ try {
77
+ // ็”จ child_process ๅœจๅญ่ฟ›็จ‹้‡Œ require๏ผŒ้ฟๅ…ๆฑกๆŸ“ไธป่ฟ›็จ‹ๆจกๅ—็ผ“ๅญ˜
78
+ const result = spawnSync(process.execPath, [
79
+ '-e',
80
+ `require(${JSON.stringify(path.join(BSQL3_DIR, 'lib', 'database.js'))})`,
81
+ ], { timeout: 5000 });
82
+ failed = result.status !== 0 && result.stderr &&
83
+ result.stderr.toString().includes('NODE_MODULE_VERSION');
84
+ } catch {
85
+ failed = true;
86
+ }
87
+
88
+ if (!failed) return;
89
+
90
+ console.log('[work-agent] Detected Node.js version mismatch for better-sqlite3.');
91
+ console.log('[work-agent] Rebuilding native module (this takes ~10 seconds)...\n');
92
+
93
+ const result = spawnSync(
94
+ 'npm',
95
+ ['rebuild', 'better-sqlite3'],
96
+ {
97
+ cwd: STANDALONE_DIR,
98
+ stdio: 'inherit',
99
+ shell: process.platform === 'win32',
100
+ }
101
+ );
102
+
103
+ if (result.status !== 0) {
104
+ console.error('\n[work-agent] Rebuild failed. Please install build tools:');
105
+ console.error(' macOS: xcode-select --install');
106
+ console.error(' Linux: apt install -y python3 make g++');
107
+ console.error(' Windows: npm install -g windows-build-tools\n');
108
+ process.exit(1);
109
+ }
110
+
111
+ console.log('\n[work-agent] Rebuild complete.\n');
112
+ }
113
+
114
+ checkAndRebuild();
115
+
116
+ // โ”€โ”€ ็กฎไฟ static / public ่ฝฏ้“พๅญ˜ๅœจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
117
+ function ensureLink(src, dst) {
118
+ if (!fs.existsSync(dst) && fs.existsSync(src)) {
119
+ try {
120
+ fs.symlinkSync(src, dst, 'junction');
121
+ } catch {
122
+ // ๅฟฝ็•ฅ
123
+ }
124
+ }
125
+ }
126
+
127
+ ensureLink(STATIC_SRC, path.join(STANDALONE_DIR, '.next', 'static'));
128
+ ensureLink(PUBLIC_SRC, path.join(STANDALONE_DIR, 'public'));
129
+
130
+ // โ”€โ”€ ็ญ‰ๅพ…ๆœๅŠกๅฐฑ็ปช โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
131
+ function waitForReady(port, retries, delay, cb) {
132
+ http.get(`http://127.0.0.1:${port}/`, () => cb(null))
133
+ .on('error', () => {
134
+ if (retries <= 0) return cb(new Error('timeout'));
135
+ setTimeout(() => waitForReady(port, retries - 1, delay, cb), delay);
136
+ });
137
+ }
138
+
139
+ // โ”€โ”€ ๅฏๅŠจๆœๅŠก โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
140
+ console.log(`\n[work-agent] Starting on http://localhost:${PORT} ...\n`);
141
+
142
+ const server = spawn(process.execPath, [STANDALONE_SERVER], {
143
+ cwd: STANDALONE_DIR,
144
+ env: { ...process.env, PORT: String(PORT), HOST, NODE_ENV: 'production' },
145
+ stdio: 'inherit',
146
+ });
147
+
148
+ server.on('error', (err) => {
149
+ console.error('[work-agent] Failed to start server:', err.message);
150
+ process.exit(1);
151
+ });
152
+
153
+ server.on('exit', (code) => process.exit(code || 0));
154
+
155
+ // โ”€โ”€ ๅฐฑ็ปชๅŽๆ‰“ๅผ€ๆต่งˆๅ™จ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
156
+ const url = `http://localhost:${PORT}`;
157
+
158
+ waitForReady(PORT, 30, 500, (err) => {
159
+ if (err) {
160
+ console.log(`[work-agent] Open your browser at: ${url}`);
161
+ return;
162
+ }
163
+ console.log(`[work-agent] Ready! Opening ${url}`);
164
+ try {
165
+ require('open')(url).catch(() => {});
166
+ } catch {
167
+ console.log(`[work-agent] Open your browser at: ${url}`);
168
+ }
169
+ });
170
+
171
+ ['SIGINT', 'SIGTERM'].forEach((sig) => {
172
+ process.on(sig, () => server.kill(sig));
173
+ });