@tekmidian/pai 0.36.2 → 0.38.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 (157) hide show
  1. package/dist/{auto-route-Byf8ENXj.mjs → auto-route-DM7GhJ8y.mjs} +2 -2
  2. package/dist/{auto-route-Byf8ENXj.mjs.map → auto-route-DM7GhJ8y.mjs.map} +1 -1
  3. package/dist/cli/index.mjs +5 -4
  4. package/dist/cli/index.mjs.map +1 -1
  5. package/dist/cli/program.d.mts.map +1 -1
  6. package/dist/cli/program.mjs +5 -4
  7. package/dist/{clusters-wZgTCYCB.mjs → clusters-Do4tEGyc.mjs} +1 -1
  8. package/dist/{clusters-wZgTCYCB.mjs.map → clusters-Do4tEGyc.mjs.map} +1 -1
  9. package/dist/{context-handover-cache-PtNvj_8D.mjs → context-handover-cache-BpUojjsi.mjs} +2 -2
  10. package/dist/{context-handover-cache-PtNvj_8D.mjs.map → context-handover-cache-BpUojjsi.mjs.map} +1 -1
  11. package/dist/daemon/index.mjs +8 -8
  12. package/dist/{daemon-BZ93KRBo.mjs → daemon-CGg1VCbA.mjs} +25 -25
  13. package/dist/{daemon-BZ93KRBo.mjs.map → daemon-CGg1VCbA.mjs.map} +1 -1
  14. package/dist/daemon-DsGGiIJM.mjs +20 -0
  15. package/dist/daemon-mcp/index.mjs +549 -1
  16. package/dist/daemon-mcp/index.mjs.map +1 -1
  17. package/dist/{detector--Gg5JRN5.mjs → detector-CMap-9vw.mjs} +1 -1
  18. package/dist/{detector--Gg5JRN5.mjs.map → detector-CMap-9vw.mjs.map} +1 -1
  19. package/dist/detector-DtLExmHN.mjs +5 -0
  20. package/dist/factory-BXzqRYVZ.mjs +3 -0
  21. package/dist/{factory-Bsp7xOpO.mjs → factory-DD2T33C9.mjs} +5 -5
  22. package/dist/{factory-Bsp7xOpO.mjs.map → factory-DD2T33C9.mjs.map} +1 -1
  23. package/dist/hooks/context-compression-hook.mjs +164 -14
  24. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  25. package/dist/hooks/initialize-session.mjs +6 -0
  26. package/dist/hooks/initialize-session.mjs.map +3 -3
  27. package/dist/hooks/load-core-context.mjs +6 -0
  28. package/dist/hooks/load-core-context.mjs.map +3 -3
  29. package/dist/hooks/load-project-context.mjs +6 -0
  30. package/dist/hooks/load-project-context.mjs.map +3 -3
  31. package/dist/hooks/post-compact-inject.mjs +6 -0
  32. package/dist/hooks/post-compact-inject.mjs.map +3 -3
  33. package/dist/hooks/route-agents-to-worker.mjs +518 -0
  34. package/dist/hooks/route-agents-to-worker.mjs.map +7 -0
  35. package/dist/hooks/status-line.mjs +400 -0
  36. package/dist/hooks/status-line.mjs.map +7 -0
  37. package/dist/hooks/stop-hook.mjs +6 -0
  38. package/dist/hooks/stop-hook.mjs.map +3 -3
  39. package/dist/hooks/subagent-stop-hook.mjs +6 -0
  40. package/dist/hooks/subagent-stop-hook.mjs.map +3 -3
  41. package/dist/hooks/sync-todo-to-md.mjs +6 -0
  42. package/dist/hooks/sync-todo-to-md.mjs.map +3 -3
  43. package/dist/hooks/update-tab-on-action.mjs +6 -0
  44. package/dist/hooks/update-tab-on-action.mjs.map +3 -3
  45. package/dist/hooks/update-tab-titles.mjs +6 -0
  46. package/dist/hooks/update-tab-titles.mjs.map +3 -3
  47. package/dist/hooks/worker-proxy.mjs +858 -0
  48. package/dist/hooks/worker-proxy.mjs.map +7 -0
  49. package/dist/hooks/worker-status-line.mjs +545 -0
  50. package/dist/hooks/worker-status-line.mjs.map +7 -0
  51. package/dist/{indexer-backend-nQZuEx6N.mjs → indexer-backend-Cox9BCo-.mjs} +1 -1
  52. package/dist/{indexer-backend-nQZuEx6N.mjs.map → indexer-backend-Cox9BCo-.mjs.map} +1 -1
  53. package/dist/{kg-entity-DbOMPdF9.mjs → kg-entity-r8duqhi9.mjs} +1 -1
  54. package/dist/{kg-entity-DbOMPdF9.mjs.map → kg-entity-r8duqhi9.mjs.map} +1 -1
  55. package/dist/{latent-ideas-Bn6A5-5P.mjs → latent-ideas-BC1oINZ-.mjs} +2 -2
  56. package/dist/{latent-ideas-Bn6A5-5P.mjs.map → latent-ideas-BC1oINZ-.mjs.map} +1 -1
  57. package/dist/{link-boost-fYjUnxCN.mjs → link-boost-QFLrJwD6.mjs} +1 -1
  58. package/dist/{link-boost-fYjUnxCN.mjs.map → link-boost-QFLrJwD6.mjs.map} +1 -1
  59. package/dist/{main-resolver-BAbhKpeX.mjs → main-resolver-DlaLOFBA.mjs} +1 -1
  60. package/dist/{main-resolver-BAbhKpeX.mjs.map → main-resolver-DlaLOFBA.mjs.map} +1 -1
  61. package/dist/{main-resolver-Dxh444GO.mjs → main-resolver-IhZo4pI0.mjs} +1 -1
  62. package/dist/{neighborhood-DpaEM991.mjs → neighborhood-D9MJ1c8f.mjs} +1 -1
  63. package/dist/{neighborhood-DpaEM991.mjs.map → neighborhood-D9MJ1c8f.mjs.map} +1 -1
  64. package/dist/{note-context-DrcY4cWm.mjs → note-context-d1wT_-GA.mjs} +1 -1
  65. package/dist/{note-context-DrcY4cWm.mjs.map → note-context-d1wT_-GA.mjs.map} +1 -1
  66. package/dist/{postgres-BVme6qX0.mjs → postgres--BjPtLa0.mjs} +1 -1
  67. package/dist/{postgres-BVme6qX0.mjs.map → postgres--BjPtLa0.mjs.map} +1 -1
  68. package/dist/{program-C-fUghPv.mjs → program-Y0hAiVy8.mjs} +851 -101
  69. package/dist/program-Y0hAiVy8.mjs.map +1 -0
  70. package/dist/providers-DUshcB-d.mjs +4031 -0
  71. package/dist/providers-DUshcB-d.mjs.map +1 -0
  72. package/dist/{query-feedback-C1T6kS18.mjs → query-feedback-BUJxgw5B.mjs} +1 -1
  73. package/dist/{query-feedback-C1T6kS18.mjs.map → query-feedback-BUJxgw5B.mjs.map} +1 -1
  74. package/dist/query-feedback-DhyLOe5S.mjs +3 -0
  75. package/dist/router-1zi8jiNF.mjs +3 -0
  76. package/dist/{router-CsDm7HvK.mjs → router-DK_sLsUL.mjs} +1 -1
  77. package/dist/{router-CsDm7HvK.mjs.map → router-DK_sLsUL.mjs.map} +1 -1
  78. package/dist/skills/Worker/SKILL.md +63 -0
  79. package/dist/{sources-D8ZdNfvK.mjs → sources-Bi7--33T.mjs} +1 -1
  80. package/dist/{sources-D8ZdNfvK.mjs.map → sources-Bi7--33T.mjs.map} +1 -1
  81. package/dist/{sqlite-D1IaR8Am.mjs → sqlite-DtaL1glm.mjs} +1 -1
  82. package/dist/{sqlite-D1IaR8Am.mjs.map → sqlite-DtaL1glm.mjs.map} +1 -1
  83. package/dist/{state-qtmrBWCm.mjs → state-8Hm9E4tW.mjs} +1 -1
  84. package/dist/{state-WaXhLr6R.mjs → state-BY2L6-vX.mjs} +1 -1
  85. package/dist/{state-WaXhLr6R.mjs.map → state-BY2L6-vX.mjs.map} +1 -1
  86. package/dist/{themes-XPkj_bfP.mjs → themes-BN0a2duq.mjs} +1 -1
  87. package/dist/{themes-XPkj_bfP.mjs.map → themes-BN0a2duq.mjs.map} +1 -1
  88. package/dist/{tools-DEt6YPfc.mjs → tools-CGPqpU3A.mjs} +1 -1
  89. package/dist/{tools-ceiy7ANX.mjs → tools-y2bJpKom.mjs} +14 -14
  90. package/dist/{tools-ceiy7ANX.mjs.map → tools-y2bJpKom.mjs.map} +1 -1
  91. package/dist/{trace-DfyGmMG_.mjs → trace-h23JCcFD.mjs} +1 -1
  92. package/dist/{trace-DfyGmMG_.mjs.map → trace-h23JCcFD.mjs.map} +1 -1
  93. package/dist/{vault-indexer-CFvlPUMB.mjs → vault-indexer-DgsPjMgs.mjs} +1 -1
  94. package/dist/{vault-indexer-CFvlPUMB.mjs.map → vault-indexer-DgsPjMgs.mjs.map} +1 -1
  95. package/dist/{work-queue-worker-B8W8_3Rn.mjs → work-queue-worker-Dva_v_pI.mjs} +4 -4
  96. package/dist/{work-queue-worker-B8W8_3Rn.mjs.map → work-queue-worker-Dva_v_pI.mjs.map} +1 -1
  97. package/dist/{work-queue-worker-HN2Ufg-L.mjs → work-queue-worker-gsKd2LJa.mjs} +4 -4
  98. package/dist/{zettelkasten-CvjmMghT.mjs → zettelkasten-vo7psPdT.mjs} +3 -3
  99. package/dist/{zettelkasten-CvjmMghT.mjs.map → zettelkasten-vo7psPdT.mjs.map} +1 -1
  100. package/docs/commands/README.md +29 -0
  101. package/docs/commands/backup.md +1 -1
  102. package/docs/commands/clear-names.md +1 -1
  103. package/docs/commands/daemon.md +1 -1
  104. package/docs/commands/db.md +1 -1
  105. package/docs/commands/end.md +1 -1
  106. package/docs/commands/help.md +1 -1
  107. package/docs/commands/identity.md +1 -1
  108. package/docs/commands/kg.md +1 -1
  109. package/docs/commands/mcp.md +1 -1
  110. package/docs/commands/memory.md +1 -1
  111. package/docs/commands/notify.md +1 -1
  112. package/docs/commands/observation.md +1 -1
  113. package/docs/commands/obsidian.md +1 -1
  114. package/docs/commands/pause.md +1 -1
  115. package/docs/commands/project.md +1 -1
  116. package/docs/commands/projects.md +1 -1
  117. package/docs/commands/registry.md +1 -1
  118. package/docs/commands/restore.md +1 -1
  119. package/docs/commands/session.md +1 -1
  120. package/docs/commands/sessions.md +1 -1
  121. package/docs/commands/setup.md +1 -1
  122. package/docs/commands/shell-init.md +1 -1
  123. package/docs/commands/skill.md +1 -1
  124. package/docs/commands/task.md +1 -1
  125. package/docs/commands/topic.md +1 -1
  126. package/docs/commands/update.md +1 -1
  127. package/docs/commands/worker.md +398 -0
  128. package/docs/commands/zettel.md +1 -1
  129. package/docs/worker.md +434 -0
  130. package/package.json +1 -1
  131. package/scripts/build-hooks.mjs +46 -1
  132. package/src/hooks/pre-compact.sh +1 -0
  133. package/src/hooks/session-autosave.sh +1 -0
  134. package/src/hooks/session-stop.sh +1 -0
  135. package/src/hooks/ts/lib/context-fill.test.ts +104 -0
  136. package/src/hooks/ts/lib/context-fill.ts +73 -0
  137. package/src/hooks/ts/lib/handover-evidence.ts +227 -0
  138. package/src/hooks/ts/lib/worker-session.test.ts +24 -0
  139. package/src/hooks/ts/lib/worker-session.ts +23 -0
  140. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +2 -0
  141. package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +2 -0
  142. package/src/hooks/ts/pre-compact/context-compression-hook.ts +17 -0
  143. package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +128 -0
  144. package/src/hooks/ts/session-start/initialize-session.ts +2 -0
  145. package/src/hooks/ts/session-start/load-core-context.ts +2 -0
  146. package/src/hooks/ts/session-start/load-project-context.ts +2 -0
  147. package/src/hooks/ts/session-start/post-compact-inject.ts +2 -0
  148. package/src/hooks/ts/stop/stop-hook.ts +2 -0
  149. package/src/hooks/ts/subagent-stop/subagent-stop-hook.ts +2 -0
  150. package/src/hooks/ts/user-prompt/update-tab-titles.ts +2 -0
  151. package/statusline-command.sh +17 -0
  152. package/dist/daemon-DRdoA489.mjs +0 -20
  153. package/dist/detector-DGAk1iBR.mjs +0 -5
  154. package/dist/factory-CrokPMk2.mjs +0 -3
  155. package/dist/program-C-fUghPv.mjs.map +0 -1
  156. package/dist/query-feedback-BBMBp96K.mjs +0 -3
  157. package/dist/router-BMkOb62X.mjs +0 -3
@@ -0,0 +1,858 @@
1
+ // src/workers/standalone/proxy.ts
2
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
3
+
4
+ // src/workers/config.ts
5
+ import { homedir as homedir2 } from "node:os";
6
+ import { join as join2 } from "node:path";
7
+
8
+ // src/config/json-store.ts
9
+ import {
10
+ existsSync,
11
+ readFileSync,
12
+ writeFileSync,
13
+ copyFileSync,
14
+ renameSync,
15
+ unlinkSync,
16
+ mkdirSync
17
+ } from "node:fs";
18
+ function readJsonStrict(path, label = path) {
19
+ if (!existsSync(path)) return {};
20
+ let raw;
21
+ try {
22
+ raw = readFileSync(path, "utf8");
23
+ } catch (e) {
24
+ throw new Error(
25
+ `Could not read ${label}: ${e instanceof Error ? e.message : String(e)}
26
+ Refusing to continue \u2014 writing now would replace its contents with ours alone.`
27
+ );
28
+ }
29
+ try {
30
+ return JSON.parse(raw);
31
+ } catch (e) {
32
+ throw new Error(
33
+ `${label} exists but is not valid JSON: ${e instanceof Error ? e.message : String(e)}
34
+ Refusing to continue \u2014 overwriting it would destroy whatever it holds.
35
+ Repair the file, or move it aside and re-run this command.`
36
+ );
37
+ }
38
+ }
39
+
40
+ // src/daemon/config.ts
41
+ import { homedir, userInfo } from "node:os";
42
+ import { join } from "node:path";
43
+
44
+ // src/notifications/types.ts
45
+ var DEFAULT_ROUTING = {
46
+ error: ["whatsapp", "macos", "ntfy", "cli"],
47
+ completion: ["whatsapp", "macos", "ntfy", "cli"],
48
+ info: ["cli"],
49
+ progress: ["cli"],
50
+ debug: []
51
+ };
52
+ var DEFAULT_CHANNELS = {
53
+ ntfy: {
54
+ enabled: false,
55
+ url: void 0,
56
+ priority: "default"
57
+ },
58
+ whatsapp: {
59
+ enabled: true,
60
+ recipient: void 0
61
+ },
62
+ macos: {
63
+ enabled: true
64
+ },
65
+ voice: {
66
+ enabled: false,
67
+ voiceName: "bm_george"
68
+ },
69
+ cli: {
70
+ enabled: true
71
+ }
72
+ };
73
+ var DEFAULT_NOTIFICATION_CONFIG = {
74
+ mode: "auto",
75
+ channels: DEFAULT_CHANNELS,
76
+ routing: DEFAULT_ROUTING
77
+ };
78
+
79
+ // src/tasks/types.ts
80
+ var DEFAULT_TASK_CONFIG = {
81
+ enabled: false,
82
+ providers: {
83
+ todoist: {
84
+ enabled: false
85
+ }
86
+ },
87
+ autoDispatch: false
88
+ };
89
+
90
+ // src/runtime-paths.ts
91
+ function paiSocketPath() {
92
+ return process.env.PAI_SOCKET_PATH ?? "/tmp/pai.sock";
93
+ }
94
+
95
+ // src/daemon/config.ts
96
+ function perUserDbName() {
97
+ const username = userInfo().username;
98
+ const safe = username.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
99
+ return `pai_${safe}`;
100
+ }
101
+ function perUserConnectionString() {
102
+ const db = perUserDbName();
103
+ return `postgresql://pai:pai@localhost:5432/${db}`;
104
+ }
105
+ var DEFAULTS = {
106
+ socketPath: paiSocketPath(),
107
+ indexIntervalSecs: 300,
108
+ embedIntervalSecs: 600,
109
+ embedOnStartup: false,
110
+ storageBackend: "sqlite",
111
+ postgres: {
112
+ connectionString: perUserConnectionString(),
113
+ maxConnections: 5,
114
+ connectionTimeoutMs: 5e3
115
+ },
116
+ embeddingModel: "Snowflake/snowflake-arctic-embed-m-v1.5",
117
+ logLevel: "info",
118
+ notifications: DEFAULT_NOTIFICATION_CONFIG,
119
+ tasks: DEFAULT_TASK_CONFIG,
120
+ // Deliberately empty. An install must not guess who the user is: a wrong
121
+ // guess here is an address that can be mailed without review.
122
+ identity: { selfEmails: [] },
123
+ search: {
124
+ mode: "keyword",
125
+ rerank: true,
126
+ recencyBoostDays: 90,
127
+ defaultLimit: 10,
128
+ snippetLength: 200
129
+ }
130
+ };
131
+ var CONFIG_DIR = join(homedir(), ".config", "pai");
132
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
133
+
134
+ // src/workers/config.ts
135
+ var PROVIDER_TAGS = [
136
+ "code",
137
+ "vision",
138
+ "image-gen",
139
+ "long-context",
140
+ "fast",
141
+ "reasoning"
142
+ ];
143
+ var DEFAULT_LOG_DIR = "~/.claude/logs/workers";
144
+ var DEFAULT_PANE = {
145
+ enabled: true,
146
+ fontSize: 13,
147
+ autoExitSecs: 60
148
+ };
149
+ var DEFAULT_ROUTING2 = {
150
+ order: [],
151
+ cooldownMinutes: 30,
152
+ retryOnQuota: true
153
+ };
154
+ function defaultWorkersConfig() {
155
+ return {
156
+ enabled: false,
157
+ active: null,
158
+ providers: {},
159
+ classes: {},
160
+ mcpSets: {},
161
+ pane: { ...DEFAULT_PANE },
162
+ logDir: DEFAULT_LOG_DIR,
163
+ routing: { ...DEFAULT_ROUTING2, order: [] }
164
+ };
165
+ }
166
+ var WorkersConfigError = class extends Error {
167
+ };
168
+ function bad(path, why) {
169
+ throw new WorkersConfigError(`workers${path}: ${why}`);
170
+ }
171
+ function str(v) {
172
+ return typeof v === "string" ? v : "";
173
+ }
174
+ function parseProvider(name, raw) {
175
+ if (typeof raw !== "object" || raw === null) bad(`.providers.${name}`, "must be an object");
176
+ const p = raw;
177
+ const protocol = p.protocol === void 0 ? "anthropic" : str(p.protocol);
178
+ if (protocol !== "anthropic" && protocol !== "openai") {
179
+ bad(`.providers.${name}.protocol`, `"${str(p.protocol)}" is neither "anthropic" nor "openai"`);
180
+ }
181
+ const engine = p.engine === void 0 ? "claude" : str(p.engine);
182
+ if (engine !== "claude" && engine !== "codex") {
183
+ bad(`.providers.${name}.engine`, `"${str(p.engine)}" is neither "claude" nor "codex"`);
184
+ }
185
+ const baseUrl = str(p.baseUrl);
186
+ if (!baseUrl && protocol !== "openai") bad(`.providers.${name}.baseUrl`, "is required");
187
+ const keyFile = p.keyFile === void 0 || p.keyFile === null || str(p.keyFile) === "" ? null : str(p.keyFile);
188
+ const modelsRaw = p.models === void 0 ? {} : p.models;
189
+ if (typeof modelsRaw !== "object" || modelsRaw === null) {
190
+ bad(`.providers.${name}.models`, "must be an object");
191
+ }
192
+ const m = modelsRaw;
193
+ const defaultModel = str(m.default);
194
+ if (!defaultModel) bad(`.providers.${name}.models.default`, "is required");
195
+ const fast = m.fast === void 0 ? void 0 : str(m.fast);
196
+ const env = {};
197
+ if (p.env !== void 0) {
198
+ if (typeof p.env !== "object" || p.env === null || Array.isArray(p.env)) {
199
+ bad(`.providers.${name}.env`, "must be an object of string values");
200
+ }
201
+ for (const [k, v] of Object.entries(p.env)) {
202
+ if (typeof v !== "string") bad(`.providers.${name}.env.${k}`, "must be a string");
203
+ env[k] = v;
204
+ }
205
+ }
206
+ const quotaSkipAt = p.quotaSkipAt === void 0 ? void 0 : p.quotaSkipAt;
207
+ if (quotaSkipAt !== void 0) {
208
+ if (typeof quotaSkipAt !== "number" || quotaSkipAt < 0 || quotaSkipAt > 100) {
209
+ bad(`.providers.${name}.quotaSkipAt`, "must be a number between 0 and 100");
210
+ }
211
+ }
212
+ const contextWindow = p.contextWindow === void 0 ? void 0 : p.contextWindow;
213
+ if (contextWindow !== void 0) {
214
+ if (typeof contextWindow !== "number" || contextWindow <= 0) {
215
+ bad(`.providers.${name}.contextWindow`, "must be a positive number of tokens");
216
+ }
217
+ }
218
+ const upstreamUrl = str(p.upstreamUrl);
219
+ if (protocol === "openai" && !upstreamUrl) {
220
+ bad(`.providers.${name}.upstreamUrl`, `is required for protocol "openai" (the Chat Completions base, e.g. "https://api.openai.com/v1")`);
221
+ }
222
+ const costTier = p.costTier === void 0 ? void 0 : p.costTier;
223
+ if (costTier !== void 0) {
224
+ if (typeof costTier !== "number" || !Number.isInteger(costTier) || costTier < 1 || costTier > 5) {
225
+ bad(`.providers.${name}.costTier`, "must be an integer 1 (cheapest) \u2026 5 (most expensive)");
226
+ }
227
+ }
228
+ let tags;
229
+ if (p.tags !== void 0) {
230
+ if (!Array.isArray(p.tags) || p.tags.some((x) => typeof x !== "string")) {
231
+ bad(`.providers.${name}.tags`, `must be an array of tags from: ${PROVIDER_TAGS.join(", ")}`);
232
+ }
233
+ for (const t of p.tags) {
234
+ if (!PROVIDER_TAGS.includes(t)) {
235
+ bad(`.providers.${name}.tags`, `"${t}" is not a tag (from: ${PROVIDER_TAGS.join(", ")})`);
236
+ }
237
+ }
238
+ tags = p.tags;
239
+ }
240
+ return {
241
+ enabled: p.enabled === void 0 ? true : p.enabled === true,
242
+ protocol,
243
+ baseUrl,
244
+ keyFile,
245
+ models: fast ? { default: defaultModel, fast } : { default: defaultModel },
246
+ env,
247
+ ...str(p.note) ? { note: str(p.note) } : {},
248
+ ...upstreamUrl ? { upstreamUrl } : {},
249
+ ...engine !== "claude" ? { engine } : {},
250
+ ...str(p.quotaProbe) ? { quotaProbe: str(p.quotaProbe) } : {},
251
+ ...quotaSkipAt !== void 0 ? { quotaSkipAt } : {},
252
+ ...contextWindow !== void 0 ? { contextWindow } : {},
253
+ ...costTier !== void 0 ? { costTier } : {},
254
+ ...tags ? { tags } : {}
255
+ };
256
+ }
257
+ function parseWorkersConfig(raw) {
258
+ const d = defaultWorkersConfig();
259
+ if (raw === void 0 || raw === null) return d;
260
+ if (typeof raw !== "object" || Array.isArray(raw)) {
261
+ bad("", "section must be an object");
262
+ }
263
+ const w = raw;
264
+ const providers = {};
265
+ if (w.providers !== void 0) {
266
+ if (typeof w.providers !== "object" || w.providers === null || Array.isArray(w.providers)) {
267
+ bad(".providers", "must be an object keyed by provider name");
268
+ }
269
+ for (const [name, p] of Object.entries(w.providers)) {
270
+ providers[name] = parseProvider(name, p);
271
+ }
272
+ }
273
+ const classes = {};
274
+ const classesRaw = w.classes !== void 0 ? w.classes : w.roles;
275
+ if (classesRaw !== void 0) {
276
+ if (typeof classesRaw !== "object" || classesRaw === null || Array.isArray(classesRaw)) {
277
+ bad(w.classes !== void 0 ? ".classes" : ".roles", "must be an object of class \u2192 provider[/alias] or {provider, mcp, maxCostTier, requireTags, order}");
278
+ }
279
+ for (const [cls, target] of Object.entries(classesRaw)) {
280
+ if (typeof target === "object" && target !== null && !Array.isArray(target)) {
281
+ const o = target;
282
+ const provider = str(o.provider);
283
+ if (o.provider !== void 0 && (!provider || provider.includes(" "))) {
284
+ bad(`.classes.${cls}.provider`, `invalid provider "${provider}"`);
285
+ }
286
+ let mcp;
287
+ if (o.mcp !== void 0) {
288
+ if (!Array.isArray(o.mcp) || o.mcp.some((x) => typeof x !== "string")) {
289
+ bad(`.classes.${cls}.mcp`, "must be an array of MCP server or set names");
290
+ }
291
+ mcp = o.mcp;
292
+ }
293
+ let maxCostTier;
294
+ if (o.maxCostTier !== void 0) {
295
+ if (typeof o.maxCostTier !== "number" || !Number.isInteger(o.maxCostTier) || o.maxCostTier < 1 || o.maxCostTier > 5) {
296
+ bad(`.classes.${cls}.maxCostTier`, "must be an integer 1 \u2026 5");
297
+ }
298
+ maxCostTier = o.maxCostTier;
299
+ }
300
+ let requireTags;
301
+ if (o.requireTags !== void 0) {
302
+ if (!Array.isArray(o.requireTags) || o.requireTags.some((x) => typeof x !== "string")) {
303
+ bad(`.classes.${cls}.requireTags`, `must be an array of tags from: ${PROVIDER_TAGS.join(", ")}`);
304
+ }
305
+ for (const t of o.requireTags) {
306
+ if (!PROVIDER_TAGS.includes(t)) {
307
+ bad(`.classes.${cls}.requireTags`, `"${t}" is not a tag (from: ${PROVIDER_TAGS.join(", ")})`);
308
+ }
309
+ }
310
+ requireTags = o.requireTags;
311
+ }
312
+ let order;
313
+ if (o.order !== void 0) {
314
+ if (!Array.isArray(o.order) || o.order.some((x) => typeof x !== "string")) {
315
+ bad(`.classes.${cls}.order`, "must be an array of provider names");
316
+ }
317
+ order = o.order;
318
+ }
319
+ const obj = {
320
+ ...provider ? { provider } : {},
321
+ ...mcp ? { mcp } : {},
322
+ ...maxCostTier !== void 0 ? { maxCostTier } : {},
323
+ ...requireTags ? { requireTags } : {},
324
+ ...order ? { order } : {}
325
+ };
326
+ classes[cls] = Object.keys(obj).length ? obj : {};
327
+ } else {
328
+ const t = str(target);
329
+ if (!t || t.includes(" ")) bad(`.classes.${cls}`, `invalid target "${t}"`);
330
+ classes[cls] = t;
331
+ }
332
+ }
333
+ }
334
+ const mcpSets = {};
335
+ if (w.mcpSets !== void 0) {
336
+ if (typeof w.mcpSets !== "object" || w.mcpSets === null || Array.isArray(w.mcpSets)) {
337
+ bad(".mcpSets", "must be an object of set name \u2192 [server names]");
338
+ }
339
+ for (const [setName, servers] of Object.entries(w.mcpSets)) {
340
+ if (!Array.isArray(servers) || servers.some((x) => typeof x !== "string")) {
341
+ bad(`.mcpSets.${setName}`, "must be an array of MCP server names");
342
+ }
343
+ mcpSets[setName] = servers;
344
+ }
345
+ }
346
+ let pane = { ...DEFAULT_PANE };
347
+ if (w.pane !== void 0) {
348
+ if (typeof w.pane !== "object" || w.pane === null) bad(".pane", "must be an object");
349
+ const pc = w.pane;
350
+ if (pc.enabled !== void 0 && typeof pc.enabled !== "boolean") bad(".pane.enabled", "must be boolean");
351
+ if (pc.fontSize !== void 0 && (typeof pc.fontSize !== "number" || pc.fontSize <= 0)) {
352
+ bad(".pane.fontSize", "must be a positive number of points");
353
+ }
354
+ if (pc.autoExitSecs !== void 0 && typeof pc.autoExitSecs !== "number") {
355
+ bad(".pane.autoExitSecs", "must be a number");
356
+ }
357
+ pane = {
358
+ enabled: pc.enabled === void 0 ? DEFAULT_PANE.enabled : pc.enabled === true,
359
+ fontSize: pc.fontSize === void 0 ? DEFAULT_PANE.fontSize : pc.fontSize,
360
+ autoExitSecs: pc.autoExitSecs === void 0 ? DEFAULT_PANE.autoExitSecs : pc.autoExitSecs
361
+ };
362
+ }
363
+ let routing = { ...DEFAULT_ROUTING2, order: [] };
364
+ if (w.routing !== void 0) {
365
+ if (typeof w.routing !== "object" || w.routing === null) bad(".routing", "must be an object");
366
+ const r = w.routing;
367
+ if (r.order !== void 0) {
368
+ if (!Array.isArray(r.order) || r.order.some((x) => typeof x !== "string")) {
369
+ bad(".routing.order", "must be an array of provider names");
370
+ }
371
+ routing.order = r.order;
372
+ }
373
+ if (r.cooldownMinutes !== void 0 && typeof r.cooldownMinutes !== "number") {
374
+ bad(".routing.cooldownMinutes", "must be a number");
375
+ }
376
+ if (r.retryOnQuota !== void 0 && typeof r.retryOnQuota !== "boolean") {
377
+ bad(".routing.retryOnQuota", "must be boolean");
378
+ }
379
+ routing = {
380
+ order: routing.order,
381
+ cooldownMinutes: r.cooldownMinutes === void 0 ? DEFAULT_ROUTING2.cooldownMinutes : r.cooldownMinutes,
382
+ retryOnQuota: r.retryOnQuota === void 0 ? DEFAULT_ROUTING2.retryOnQuota : r.retryOnQuota === true
383
+ };
384
+ }
385
+ const active = w.active === void 0 || w.active === null ? null : str(w.active);
386
+ if (active !== null && active !== "auto" && !(active in providers)) {
387
+ }
388
+ return {
389
+ enabled: w.enabled === void 0 ? d.enabled : w.enabled === true,
390
+ active,
391
+ providers,
392
+ classes,
393
+ mcpSets,
394
+ pane,
395
+ logDir: str(w.logDir) || d.logDir,
396
+ routing
397
+ };
398
+ }
399
+ function readWorkersSection() {
400
+ const raw = readJsonStrict(CONFIG_FILE, "~/.config/pai/config.json");
401
+ return { raw, workers: parseWorkersConfig(raw.workers) };
402
+ }
403
+ function expandHome(p) {
404
+ if (p === "~" || p.startsWith("~/")) return join2(homedir2(), p.slice(1));
405
+ return p;
406
+ }
407
+ function providerKeyPath(p) {
408
+ return p.keyFile ? expandHome(p.keyFile) : null;
409
+ }
410
+
411
+ // src/workers/paths.ts
412
+ function workersLogDir(config) {
413
+ return expandHome(config.logDir);
414
+ }
415
+
416
+ // src/workers/proxy/server.ts
417
+ import { createServer } from "node:http";
418
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "node:fs";
419
+ import { dirname, join as join3 } from "node:path";
420
+
421
+ // src/workers/proxy/translate.ts
422
+ function systemText(system) {
423
+ if (typeof system === "string") return system;
424
+ if (!Array.isArray(system)) return "";
425
+ return system.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n").trim();
426
+ }
427
+ function toolCallFromBlock(b) {
428
+ return {
429
+ id: b.id ?? "",
430
+ type: "function",
431
+ function: { name: b.name ?? "", arguments: JSON.stringify(b.input ?? {}) }
432
+ };
433
+ }
434
+ function toolResultText(b) {
435
+ if (typeof b.content === "string") return b.content;
436
+ if (Array.isArray(b.content)) {
437
+ return b.content.map((x) => typeof x === "object" && x !== null && "text" in x ? String(x.text ?? "") : "").join("\n");
438
+ }
439
+ return "";
440
+ }
441
+ function anthropicToOpenAi(req, model) {
442
+ const messages = [];
443
+ const sys = systemText(req.system);
444
+ if (sys) messages.push({ role: "system", content: sys });
445
+ for (const m of req.messages ?? []) {
446
+ const blocks = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content;
447
+ if (m.role === "assistant") {
448
+ const text = (blocks ?? []).filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
449
+ const calls = (blocks ?? []).filter((b) => b.type === "tool_use").map(toolCallFromBlock);
450
+ const msg = { role: "assistant", content: text || null };
451
+ if (calls.length) msg.tool_calls = calls;
452
+ messages.push(msg);
453
+ } else {
454
+ const texts = [];
455
+ for (const b of blocks ?? []) {
456
+ if (b.type === "tool_result") {
457
+ messages.push({
458
+ role: "tool",
459
+ tool_call_id: b.tool_use_id ?? "",
460
+ content: toolResultText(b)
461
+ });
462
+ } else if (b.type === "text" && (b.text ?? "").trim()) {
463
+ texts.push(b.text ?? "");
464
+ }
465
+ }
466
+ if (texts.length) messages.push({ role: "user", content: texts.join("\n") });
467
+ }
468
+ }
469
+ const out = {
470
+ model,
471
+ max_tokens: req.max_tokens ?? 8192,
472
+ messages
473
+ };
474
+ if (req.temperature !== void 0) out.temperature = req.temperature;
475
+ if (req.stop_sequences?.length) out.stop = req.stop_sequences;
476
+ if (req.stream) {
477
+ out.stream = true;
478
+ out.stream_options = { include_usage: true };
479
+ }
480
+ if (req.tools?.length) {
481
+ out.tools = req.tools.map((t) => ({
482
+ type: "function",
483
+ function: {
484
+ name: t.name ?? "",
485
+ description: t.description ?? "",
486
+ parameters: t.input_schema ?? { type: "object", properties: {} }
487
+ }
488
+ }));
489
+ }
490
+ return out;
491
+ }
492
+ function stopReasonFrom(finish) {
493
+ switch (finish) {
494
+ case "tool_calls":
495
+ return "tool_use";
496
+ case "length":
497
+ return "max_tokens";
498
+ case "stop":
499
+ return "stop_sequence";
500
+ default:
501
+ return "end_turn";
502
+ }
503
+ }
504
+ function openAiToAnthropic(res, model) {
505
+ const choice = res.choices?.[0];
506
+ const msg = choice?.message;
507
+ const content = [];
508
+ if (msg?.content) content.push({ type: "text", text: msg.content });
509
+ for (const call of msg?.tool_calls ?? []) {
510
+ let input = {};
511
+ try {
512
+ input = JSON.parse(call.function?.arguments || "{}");
513
+ } catch {
514
+ input = { _raw: call.function?.arguments ?? "" };
515
+ }
516
+ content.push({ type: "tool_use", id: call.id ?? "", name: call.function?.name ?? "", input });
517
+ }
518
+ return {
519
+ id: `msg_${res.id ?? Date.now().toString(36)}`,
520
+ type: "message",
521
+ role: "assistant",
522
+ model: res.model ?? model,
523
+ content,
524
+ stop_reason: stopReasonFrom(choice?.finish_reason),
525
+ stop_sequence: null,
526
+ usage: {
527
+ input_tokens: res.usage?.prompt_tokens ?? 0,
528
+ output_tokens: res.usage?.completion_tokens ?? 0
529
+ }
530
+ };
531
+ }
532
+ function anthropicError(status, upstreamBody) {
533
+ let message = upstreamBody.slice(0, 2e3);
534
+ let upstreamType = "";
535
+ try {
536
+ const parsed = JSON.parse(upstreamBody);
537
+ message = parsed.error?.message ?? parsed.message ?? message;
538
+ upstreamType = parsed.error?.type ?? "";
539
+ } catch {
540
+ }
541
+ let type;
542
+ if (status === 429) type = "rate_limit_error";
543
+ else if (status === 401 || status === 403) type = "authentication_error";
544
+ else if (status === 400 || status === 404 || status === 422) type = "invalid_request_error";
545
+ else if (status === 402 || status >= 500) type = "api_error";
546
+ else if (upstreamType) type = upstreamType;
547
+ else type = "api_error";
548
+ return { type: "error", error: { type, message } };
549
+ }
550
+ var OpenAiStreamTranslator = class _OpenAiStreamTranslator {
551
+ messageStarted = false;
552
+ openBlock = null;
553
+ toolCalls = /* @__PURE__ */ new Map();
554
+ nextBlockIndex = 0;
555
+ outputTokens = 0;
556
+ stopReason = "end_turn";
557
+ finished = false;
558
+ model;
559
+ id;
560
+ constructor(model, id = `msg_${Date.now().toString(36)}`) {
561
+ this.model = model;
562
+ this.id = id;
563
+ }
564
+ /** SSE wire form of one Anthropic stream event. */
565
+ static sse(event, data) {
566
+ return `event: ${event}
567
+ data: ${JSON.stringify(data)}
568
+
569
+ `;
570
+ }
571
+ /** Consume one parsed OpenAI chunk; returns SSE text to forward (may be ""). */
572
+ feed(chunk) {
573
+ if (this.finished) return "";
574
+ const out = [];
575
+ if (!this.messageStarted) {
576
+ this.messageStarted = true;
577
+ out.push(
578
+ _OpenAiStreamTranslator.sse("message_start", {
579
+ type: "message_start",
580
+ message: {
581
+ id: this.id,
582
+ type: "message",
583
+ role: "assistant",
584
+ model: this.model,
585
+ content: [],
586
+ stop_reason: null,
587
+ stop_sequence: null,
588
+ usage: { input_tokens: chunk.usage?.prompt_tokens ?? 0, output_tokens: 0 }
589
+ }
590
+ })
591
+ );
592
+ }
593
+ if (chunk.usage?.completion_tokens) this.outputTokens = chunk.usage.completion_tokens;
594
+ const choice = chunk.choices?.[0];
595
+ if (choice) {
596
+ const text = choice.delta?.content;
597
+ if (typeof text === "string" && text) {
598
+ if (!this.openBlock) {
599
+ this.openBlock = { index: this.nextBlockIndex++, kind: "text" };
600
+ out.push(
601
+ _OpenAiStreamTranslator.sse("content_block_start", {
602
+ type: "content_block_start",
603
+ index: this.openBlock.index,
604
+ content_block: { type: "text", text: "" }
605
+ })
606
+ );
607
+ }
608
+ out.push(
609
+ _OpenAiStreamTranslator.sse("content_block_delta", {
610
+ type: "content_block_delta",
611
+ index: this.openBlock.index,
612
+ delta: { type: "text_delta", text }
613
+ })
614
+ );
615
+ }
616
+ for (const call of choice.delta?.tool_calls ?? []) {
617
+ const idx = call.index ?? 0;
618
+ let acc = this.toolCalls.get(idx);
619
+ if (!acc) {
620
+ acc = { id: call.id ?? "", name: "", args: "", blockIndex: null };
621
+ this.toolCalls.set(idx, acc);
622
+ }
623
+ if (call.id) acc.id = call.id;
624
+ if (call.function?.name) acc.name += call.function.name;
625
+ if (call.function?.arguments) acc.args += call.function.arguments;
626
+ if (acc.blockIndex === null && (acc.id || acc.name)) {
627
+ this.closeTextBlock(out);
628
+ acc.blockIndex = this.nextBlockIndex++;
629
+ out.push(
630
+ _OpenAiStreamTranslator.sse("content_block_start", {
631
+ type: "content_block_start",
632
+ index: acc.blockIndex,
633
+ content_block: { type: "tool_use", id: acc.id, name: acc.name, input: {} }
634
+ })
635
+ );
636
+ }
637
+ if (acc.blockIndex !== null && call.function?.arguments) {
638
+ out.push(
639
+ _OpenAiStreamTranslator.sse("content_block_delta", {
640
+ type: "content_block_delta",
641
+ index: acc.blockIndex,
642
+ delta: { type: "input_json_delta", partial_json: call.function.arguments }
643
+ })
644
+ );
645
+ }
646
+ }
647
+ if (choice.finish_reason) this.stopReason = stopReasonFrom(choice.finish_reason);
648
+ }
649
+ return out.join("");
650
+ }
651
+ closeTextBlock(out) {
652
+ if (!this.openBlock) return;
653
+ out.push(
654
+ _OpenAiStreamTranslator.sse("content_block_stop", {
655
+ type: "content_block_stop",
656
+ index: this.openBlock.index
657
+ })
658
+ );
659
+ this.openBlock = null;
660
+ }
661
+ /** Close everything and emit the tail events. Single call at stream end. */
662
+ finish() {
663
+ if (this.finished) return "";
664
+ this.finished = true;
665
+ const out = [];
666
+ this.closeTextBlock(out);
667
+ for (const acc of [...this.toolCalls.values()].sort((a, b) => (a.blockIndex ?? 0) - (b.blockIndex ?? 0))) {
668
+ if (acc.blockIndex === null) continue;
669
+ out.push(
670
+ _OpenAiStreamTranslator.sse("content_block_stop", {
671
+ type: "content_block_stop",
672
+ index: acc.blockIndex
673
+ })
674
+ );
675
+ }
676
+ out.push(
677
+ _OpenAiStreamTranslator.sse("message_delta", {
678
+ type: "message_delta",
679
+ delta: { stop_reason: this.stopReason, stop_sequence: null },
680
+ usage: { output_tokens: this.outputTokens }
681
+ })
682
+ );
683
+ out.push(_OpenAiStreamTranslator.sse("message_stop", { type: "message_stop" }));
684
+ return out.join("");
685
+ }
686
+ };
687
+
688
+ // src/workers/proxy/server.ts
689
+ var DEFAULT_PROXY_PORT = 8797;
690
+ var HOST = "127.0.0.1";
691
+ var configProviders = () => readWorkersSection().workers.providers;
692
+ function proxyPidPath(logDir2) {
693
+ return join3(logDir2, "proxy.pid");
694
+ }
695
+ function createProxyServer(opts = {}) {
696
+ const resolveProvider = opts.resolveProvider ?? configProviders;
697
+ return createServer((req, res) => {
698
+ const url = new URL(req.url ?? "/", `http://${HOST}`);
699
+ if (req.method === "GET" && url.pathname === "/healthz") {
700
+ res.writeHead(200, { "content-type": "text/plain" });
701
+ res.end("ok");
702
+ return;
703
+ }
704
+ const m = url.pathname.match(/^\/([a-zA-Z0-9_-]+)\/(v1\/)?messages$/);
705
+ if (req.method !== "POST" || !m) {
706
+ res.writeHead(404, { "content-type": "application/json" });
707
+ res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));
708
+ return;
709
+ }
710
+ const providerName = m[1];
711
+ const chunks = [];
712
+ req.on("data", (c) => chunks.push(c));
713
+ req.on("end", () => {
714
+ void handleMessages(providerName, Buffer.concat(chunks).toString("utf8"), resolveProvider, res, opts);
715
+ });
716
+ req.on("error", () => {
717
+ res.writeHead(400, { "content-type": "application/json" });
718
+ res.end(JSON.stringify(anthropicError(400, "request body read failed")));
719
+ });
720
+ });
721
+ }
722
+ async function handleMessages(providerName, bodyText, resolveProvider, res, opts) {
723
+ const fail = (status, message) => {
724
+ res.writeHead(status, { "content-type": "application/json" });
725
+ res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));
726
+ };
727
+ let reqBody;
728
+ try {
729
+ reqBody = JSON.parse(bodyText);
730
+ } catch {
731
+ fail(400, "request body is not valid JSON");
732
+ return;
733
+ }
734
+ const provider = resolveProvider()[providerName];
735
+ if (!provider) {
736
+ fail(404, `no worker provider named "${providerName}"`);
737
+ return;
738
+ }
739
+ if (provider.protocol !== "openai" || !provider.upstreamUrl) {
740
+ fail(400, `provider "${providerName}" is not an openai-protocol provider`);
741
+ return;
742
+ }
743
+ const model = provider.models.default;
744
+ const openaiBody = anthropicToOpenAi(reqBody, model);
745
+ opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);
746
+ let upstream;
747
+ try {
748
+ const headers = { "content-type": "application/json" };
749
+ const keyPath = providerKeyPath(provider);
750
+ if (keyPath) {
751
+ try {
752
+ headers.authorization = `Bearer ${readFileSync2(keyPath, "utf8").trim()}`;
753
+ } catch {
754
+ fail(500, `key file not readable: ${keyPath}`);
755
+ return;
756
+ }
757
+ }
758
+ upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {
759
+ method: "POST",
760
+ headers,
761
+ body: JSON.stringify(openaiBody)
762
+ });
763
+ } catch (e) {
764
+ fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);
765
+ return;
766
+ }
767
+ if (!upstream.ok) {
768
+ const text = await upstream.text().catch(() => "");
769
+ res.writeHead(upstream.status, { "content-type": "application/json" });
770
+ res.end(JSON.stringify(anthropicError(upstream.status, text)));
771
+ return;
772
+ }
773
+ if (reqBody.stream) {
774
+ res.writeHead(200, {
775
+ "content-type": "text/event-stream",
776
+ "cache-control": "no-cache",
777
+ connection: "keep-alive"
778
+ });
779
+ const translator = new OpenAiStreamTranslator(model);
780
+ let buf = "";
781
+ if (!upstream.body) {
782
+ res.end(translator.finish());
783
+ return;
784
+ }
785
+ const reader = upstream.body.getReader();
786
+ try {
787
+ for (; ; ) {
788
+ const { done, value } = await reader.read();
789
+ if (done) break;
790
+ buf += new TextDecoder().decode(value, { stream: true });
791
+ let nl;
792
+ while ((nl = buf.indexOf("\n")) >= 0) {
793
+ const line = buf.slice(0, nl).trim();
794
+ buf = buf.slice(nl + 1);
795
+ if (!line.startsWith("data:")) continue;
796
+ const payload = line.slice(5).trim();
797
+ if (payload === "[DONE]") {
798
+ res.write(translator.finish());
799
+ } else {
800
+ try {
801
+ res.write(translator.feed(JSON.parse(payload)));
802
+ } catch {
803
+ }
804
+ }
805
+ }
806
+ }
807
+ } finally {
808
+ res.write(translator.finish());
809
+ res.end();
810
+ }
811
+ return;
812
+ }
813
+ const json = await upstream.json().catch(() => null);
814
+ if (!json) {
815
+ fail(502, "upstream returned a non-JSON body");
816
+ return;
817
+ }
818
+ res.writeHead(200, { "content-type": "application/json" });
819
+ res.end(JSON.stringify(openAiToAnthropic(json, model)));
820
+ }
821
+ function listenProxy(server2, port2 = DEFAULT_PROXY_PORT) {
822
+ return new Promise((resolve, reject) => {
823
+ server2.once("error", reject);
824
+ server2.listen(port2, HOST, () => {
825
+ const addr = server2.address();
826
+ resolve(typeof addr === "object" && addr ? addr.port : port2);
827
+ });
828
+ });
829
+ }
830
+
831
+ // src/workers/standalone/proxy.ts
832
+ var portFlag = process.argv.indexOf("--port");
833
+ var port = portFlag > 0 ? parseInt(process.argv[portFlag + 1], 10) || DEFAULT_PROXY_PORT : DEFAULT_PROXY_PORT;
834
+ var logDir = "";
835
+ try {
836
+ logDir = workersLogDir(readWorkersSection().workers);
837
+ } catch {
838
+ logDir = "";
839
+ }
840
+ var server = createProxyServer();
841
+ listenProxy(server, port).then((bound) => {
842
+ if (logDir) {
843
+ mkdirSync3(logDir, { recursive: true });
844
+ writeFileSync3(proxyPidPath(logDir), `${process.pid}
845
+ `, "utf8");
846
+ }
847
+ process.stderr.write(`pai worker proxy listening on http://127.0.0.1:${bound}
848
+ `);
849
+ }).catch((e) => {
850
+ process.stderr.write(`pai worker proxy: cannot listen on ${port}: ${String(e)}
851
+ `);
852
+ process.exit(1);
853
+ });
854
+ process.once("SIGTERM", () => {
855
+ server.close(() => process.exit(0));
856
+ setTimeout(() => process.exit(0), 1500).unref();
857
+ });
858
+ //# sourceMappingURL=worker-proxy.mjs.map