@baique/pi-web-sky 0.1.48 → 0.1.49

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 (70) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +17 -17
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/required-server-files.js +1 -1
  5. package/.next/required-server-files.json +1 -1
  6. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +1 -1
  14. package/.next/server/app/_not-found.rsc +2 -2
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
  19. package/.next/server/app/api/app-update/route.js +1 -1
  20. package/.next/server/app/api/board-assets/[...path]/route.js +2 -2
  21. package/.next/server/app/api/board-assets/route.js +2 -2
  22. package/.next/server/app/api/boards/reorder/route.js +2 -2
  23. package/.next/server/app/api/boards/route.js +2 -2
  24. package/.next/server/app/api/cwd/validate/route.js +2 -2
  25. package/.next/server/app/api/default-cwd/route.js +2 -2
  26. package/.next/server/app/api/file-index/route.js +2 -2
  27. package/.next/server/app/api/files/[...path]/route.js +2 -2
  28. package/.next/server/app/api/mcp/route.js +2 -2
  29. package/.next/server/app/api/models/route.js +2 -2
  30. package/.next/server/app/api/plugins/route.js +2 -2
  31. package/.next/server/app/api/search/route.js +2 -2
  32. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
  33. package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
  34. package/.next/server/app/api/sessions/summary/route.js +2 -2
  35. package/.next/server/app/api/skills/install/route.js +2 -2
  36. package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
  37. package/.next/server/app/api/task-card-questions/route.js +1 -1
  38. package/.next/server/app/api/tasks/[id]/assign-session/route.js +2 -2
  39. package/.next/server/app/api/tasks/reorder/route.js +2 -2
  40. package/.next/server/app/api/tasks/route.js +2 -2
  41. package/.next/server/app/api/worktrees/route.js +2 -2
  42. package/.next/server/app/index.html +1 -1
  43. package/.next/server/app/index.rsc +4 -4
  44. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  46. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  47. package/.next/server/app/page.js +1 -1
  48. package/.next/server/app/page_client-reference-manifest.js +1 -1
  49. package/.next/server/app-paths-manifest.json +17 -17
  50. package/.next/server/chunks/3500.js +2 -2
  51. package/.next/server/chunks/4226.js +2 -2
  52. package/.next/server/chunks/5794.js +2 -2
  53. package/.next/server/chunks/5940.js +2 -2
  54. package/.next/server/chunks/6525.js +2 -2
  55. package/.next/server/middleware-build-manifest.js +1 -1
  56. package/.next/server/pages/404.html +1 -1
  57. package/.next/server/pages/500.html +1 -1
  58. package/.next/static/chunks/app/{layout-34c3afa7e57644fe.js → layout-eb4df7493f8710df.js} +1 -1
  59. package/.next/static/chunks/app/{page-72aa0bf9f2c8b93d.js → page-6ff5ffc29de8ba5f.js} +1 -1
  60. package/.next/trace +7 -7
  61. package/.next/trace-build +1 -1
  62. package/bin/pi-web-options.js +1 -1
  63. package/lib/pi-web-options.test.mjs +3 -3
  64. package/lib/project-command-env.test.mjs +4 -4
  65. package/lib/request-security.test.mjs +50 -50
  66. package/lib/request-security.ts +1 -1
  67. package/lib/sqlite-db.ts +5 -0
  68. package/package.json +1 -1
  69. /package/.next/static/{m9gWh8uFYWdcMxBdc3tY5 → -gLK6QyBXk9SPd4ajO5tR}/_buildManifest.js +0 -0
  70. /package/.next/static/{m9gWh8uFYWdcMxBdc3tY5 → -gLK6QyBXk9SPd4ajO5tR}/_ssgManifest.js +0 -0
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-webpack","duration":10257289,"timestamp":767316688167,"id":14,"parentId":1,"tags":{},"startTime":1789029807990,"traceId":"1288c02042c822d4"},{"name":"run-typescript","duration":7893653,"timestamp":767326948300,"id":794,"parentId":1,"tags":{},"startTime":1789029818250,"traceId":"1288c02042c822d4"},{"name":"static-check","duration":1894277,"timestamp":767334849106,"id":797,"parentId":1,"tags":{},"startTime":1789029826151,"traceId":"1288c02042c822d4"},{"name":"static-generation","duration":2639059,"timestamp":767336903032,"id":941,"parentId":1,"tags":{},"startTime":1789029828205,"traceId":"1288c02042c822d4"},{"name":"collect-build-traces","duration":15906223,"timestamp":767336744974,"id":938,"parentId":1,"tags":{},"startTime":1789029828047,"traceId":"1288c02042c822d4"},{"name":"telemetry-flush","duration":19,"timestamp":767352653174,"id":950,"parentId":1,"tags":{},"startTime":1789029843955,"traceId":"1288c02042c822d4"},{"name":"next-build","duration":36107053,"timestamp":767316546144,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1789029807848,"traceId":"1288c02042c822d4"}]
1
+ [{"name":"run-webpack","duration":8981522,"timestamp":768123845995,"id":14,"parentId":1,"tags":{},"startTime":1789030615100,"traceId":"ff0372cbd3f70d5f"},{"name":"run-typescript","duration":8270408,"timestamp":768132829087,"id":772,"parentId":1,"tags":{},"startTime":1789030624083,"traceId":"ff0372cbd3f70d5f"},{"name":"static-check","duration":1960226,"timestamp":768141106442,"id":775,"parentId":1,"tags":{},"startTime":1789030632360,"traceId":"ff0372cbd3f70d5f"},{"name":"static-generation","duration":2740074,"timestamp":768143204314,"id":919,"parentId":1,"tags":{},"startTime":1789030634458,"traceId":"ff0372cbd3f70d5f"},{"name":"collect-build-traces","duration":16174753,"timestamp":768143068236,"id":916,"parentId":1,"tags":{},"startTime":1789030634322,"traceId":"ff0372cbd3f70d5f"},{"name":"telemetry-flush","duration":20,"timestamp":768159244994,"id":928,"parentId":1,"tags":{},"startTime":1789030650499,"traceId":"ff0372cbd3f70d5f"},{"name":"next-build","duration":35492403,"timestamp":768123752616,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1789030615007,"traceId":"ff0372cbd3f70d5f"}]
@@ -34,7 +34,7 @@ function parseLaunchOptions(args = process.argv.slice(2), env = process.env) {
34
34
  });
35
35
 
36
36
  return {
37
- port: normalizePort(cliArgs.port ?? env.PORT ?? "30141"),
37
+ port: normalizePort(cliArgs.port ?? env.PORT ?? "30143"),
38
38
  hostname: cliArgs.hostname ?? env.PI_WEB_HOSTNAME ?? "127.0.0.1",
39
39
  openBrowser: !cliArgs["no-open"] && !isEnabled(env.PI_WEB_NO_OPEN),
40
40
  };
@@ -7,7 +7,7 @@ const { parseLaunchOptions } = require("../bin/pi-web-options.js");
7
7
 
8
8
  test("opens the browser by default", () => {
9
9
  assert.deepEqual(parseLaunchOptions([], {}), {
10
- port: "30141",
10
+ port: "30143",
11
11
  hostname: "127.0.0.1",
12
12
  openBrowser: true,
13
13
  });
@@ -42,11 +42,11 @@ test("preserves port and hostname options", () => {
42
42
 
43
43
  test("rejects port values that could inject cmd arguments", () => {
44
44
  assert.throws(
45
- () => parseLaunchOptions(["-p", "30141&whoami"], {}),
45
+ () => parseLaunchOptions(["-p", "30143&whoami"], {}),
46
46
  /Port must be a non-negative integer/,
47
47
  );
48
48
  assert.throws(
49
- () => parseLaunchOptions([], { PORT: "30141&whoami" }),
49
+ () => parseLaunchOptions([], { PORT: "30143&whoami" }),
50
50
  /Port must be a non-negative integer/,
51
51
  );
52
52
  });
@@ -16,7 +16,7 @@ const {
16
16
  } = await createJiti(import.meta.url).import("./project-command-env.ts");
17
17
 
18
18
  const HOST_ENVIRONMENT = {
19
- PORT: "30141",
19
+ PORT: "30143",
20
20
  NODE_ENV: "production",
21
21
  NEXT_RUNTIME: "nodejs",
22
22
  NEXT_PRIVATE_WORKER: "1",
@@ -41,7 +41,7 @@ test("sanitizes host variables using platform casing rules", () => {
41
41
  assert.deepEqual(
42
42
  sanitizeProjectCommandEnvironment(
43
43
  {
44
- Port: "30141",
44
+ Port: "30143",
45
45
  node_env: "production",
46
46
  Next_Runtime: "nodejs",
47
47
  NEXT_PUBLIC_FLAG: "1",
@@ -54,7 +54,7 @@ test("sanitizes host variables using platform casing rules", () => {
54
54
  assert.deepEqual(
55
55
  sanitizeProjectCommandEnvironment(
56
56
  {
57
- PORT: "30141",
57
+ PORT: "30143",
58
58
  Port: "project-value",
59
59
  NODE_ENV: "production",
60
60
  node_env: "project-mode",
@@ -80,7 +80,7 @@ test("agent bash removes host variables while preserving SDK and user environmen
80
80
  PI_USER_SETTING: process.env.PI_USER_SETTING,
81
81
  };
82
82
  Object.assign(process.env, {
83
- PORT: "30141",
83
+ PORT: "30143",
84
84
  NODE_ENV: "production",
85
85
  NEXT_RUNTIME: "nodejs",
86
86
  NEXT_PRIVATE_WORKER: "1",
@@ -7,27 +7,27 @@ async function loadSubject() {
7
7
 
8
8
  test("allows same-origin and non-browser API requests", async () => {
9
9
  const { isApiRequestAllowed } = await loadSubject();
10
- assert.equal(isApiRequestAllowed(new Request("http://localhost:30141/api/test", {
10
+ assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
11
11
  method: "POST",
12
12
  headers: {
13
- host: "localhost:30141",
14
- origin: "http://localhost:30141",
13
+ host: "localhost:30143",
14
+ origin: "http://localhost:30143",
15
15
  "sec-fetch-site": "same-origin",
16
16
  },
17
17
  })), true);
18
- assert.equal(isApiRequestAllowed(new Request("http://localhost:30141/api/test", {
18
+ assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
19
19
  method: "POST",
20
- headers: { host: "localhost:30141" },
20
+ headers: { host: "localhost:30143" },
21
21
  })), true);
22
22
  });
23
23
 
24
24
  test("allows LAN same-origin requests when Next.js uses an internal localhost URL", async () => {
25
25
  const { isApiRequestAllowed } = await loadSubject();
26
- const request = new Request("http://localhost:30141/api/test", {
26
+ const request = new Request("http://localhost:30143/api/test", {
27
27
  method: "POST",
28
28
  headers: {
29
- host: "192.168.32.7:30141",
30
- origin: "http://192.168.32.7:30141",
29
+ host: "192.168.32.7:30143",
30
+ origin: "http://192.168.32.7:30143",
31
31
  "sec-fetch-site": "same-origin",
32
32
  },
33
33
  });
@@ -36,19 +36,19 @@ test("allows LAN same-origin requests when Next.js uses an internal localhost UR
36
36
 
37
37
  test("allows IPv6 and an explicitly configured hostname", async () => {
38
38
  const { isApiRequestAllowed } = await loadSubject();
39
- const ipv6 = new Request("http://localhost:30141/api/test", {
39
+ const ipv6 = new Request("http://localhost:30143/api/test", {
40
40
  method: "POST",
41
41
  headers: {
42
- host: "[::1]:30141",
43
- origin: "http://[::1]:30141",
42
+ host: "[::1]:30143",
43
+ origin: "http://[::1]:30143",
44
44
  "sec-fetch-site": "same-origin",
45
45
  },
46
46
  });
47
- const configured = new Request("http://localhost:30141/api/test", {
47
+ const configured = new Request("http://localhost:30143/api/test", {
48
48
  method: "POST",
49
49
  headers: {
50
- host: "pi-web.internal:30141",
51
- origin: "http://pi-web.internal:30141",
50
+ host: "pi-web.internal:30143",
51
+ origin: "http://pi-web.internal:30143",
52
52
  "sec-fetch-site": "same-origin",
53
53
  },
54
54
  });
@@ -58,16 +58,16 @@ test("allows IPv6 and an explicitly configured hostname", async () => {
58
58
 
59
59
  test("rejects cross-origin browser API requests", async () => {
60
60
  const { isApiRequestAllowed, shouldCheckApiRequestOrigin } = await loadSubject();
61
- const post = new Request("http://localhost:30141/api/test", {
61
+ const post = new Request("http://localhost:30143/api/test", {
62
62
  method: "POST",
63
63
  headers: {
64
- host: "localhost:30141",
64
+ host: "localhost:30143",
65
65
  origin: "https://attacker.example",
66
66
  "sec-fetch-site": "cross-site",
67
67
  },
68
68
  });
69
- const crossSiteGet = new Request("http://localhost:30141/api/sessions", {
70
- headers: { host: "localhost:30141", "sec-fetch-site": "cross-site" },
69
+ const crossSiteGet = new Request("http://localhost:30143/api/sessions", {
70
+ headers: { host: "localhost:30143", "sec-fetch-site": "cross-site" },
71
71
  });
72
72
  assert.equal(shouldCheckApiRequestOrigin(post), true);
73
73
  assert.equal(isApiRequestAllowed(post), false);
@@ -77,10 +77,10 @@ test("rejects cross-origin browser API requests", async () => {
77
77
 
78
78
  test("does not globally trust opaque iframe or alternate loopback origins", async () => {
79
79
  const { isApiRequestAllowed } = await loadSubject();
80
- const previewUrl = "http://localhost:30141/api/files/tmp/test.docx?type=preview";
80
+ const previewUrl = "http://localhost:30143/api/files/tmp/test.docx?type=preview";
81
81
  const opaqueIframe = new Request(previewUrl, {
82
82
  headers: {
83
- host: "localhost:30141",
83
+ host: "localhost:30143",
84
84
  origin: "null",
85
85
  "sec-fetch-site": "cross-site",
86
86
  "sec-fetch-mode": "navigate",
@@ -89,8 +89,8 @@ test("does not globally trust opaque iframe or alternate loopback origins", asyn
89
89
  });
90
90
  const alternateLoopback = new Request(previewUrl, {
91
91
  headers: {
92
- host: "localhost:30141",
93
- origin: "http://127.0.0.1:30141",
92
+ host: "localhost:30143",
93
+ origin: "http://127.0.0.1:30143",
94
94
  "sec-fetch-site": "cross-site",
95
95
  },
96
96
  });
@@ -102,7 +102,7 @@ test("does not globally trust opaque iframe or alternate loopback origins", asyn
102
102
  test("allows only user-initiated session export document navigations from a PWA", async () => {
103
103
  const { isApiRequestAllowed } = await loadSubject();
104
104
  const navigationHeaders = {
105
- host: "127.0.0.1:30141",
105
+ host: "127.0.0.1:30143",
106
106
  "sec-fetch-site": "cross-site",
107
107
  "sec-fetch-mode": "navigate",
108
108
  "sec-fetch-dest": "document",
@@ -110,19 +110,19 @@ test("allows only user-initiated session export document navigations from a PWA"
110
110
  };
111
111
 
112
112
  assert.equal(isApiRequestAllowed(new Request(
113
- "http://127.0.0.1:30141/api/sessions/session-id/export?inline=1",
113
+ "http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
114
114
  { headers: navigationHeaders },
115
115
  )), true);
116
116
  assert.equal(isApiRequestAllowed(new Request(
117
- "http://127.0.0.1:30141/api/sessions",
117
+ "http://127.0.0.1:30143/api/sessions",
118
118
  { headers: navigationHeaders },
119
119
  )), false);
120
120
  assert.equal(isApiRequestAllowed(new Request(
121
- "http://127.0.0.1:30141/api/sessions/session-id/export?inline=1",
121
+ "http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
122
122
  { headers: { ...navigationHeaders, "sec-fetch-dest": "empty" } },
123
123
  )), false);
124
124
  assert.equal(isApiRequestAllowed(new Request(
125
- "http://127.0.0.1:30141/api/sessions/session-id/export?inline=1",
125
+ "http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
126
126
  {
127
127
  headers: {
128
128
  ...navigationHeaders,
@@ -131,21 +131,21 @@ test("allows only user-initiated session export document navigations from a PWA"
131
131
  },
132
132
  )), false);
133
133
  assert.equal(isApiRequestAllowed(new Request(
134
- "http://127.0.0.1:30141/api/sessions/session-id/export?inline=1",
134
+ "http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
135
135
  { method: "POST", headers: navigationHeaders },
136
136
  )), false);
137
137
  assert.equal(isApiRequestAllowed(new Request(
138
- "http://127.0.0.1:30141/api/sessions/session-id/export?inline=1",
139
- { headers: { ...navigationHeaders, host: "attacker.example:30141" } },
138
+ "http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
139
+ { headers: { ...navigationHeaders, host: "attacker.example:30143" } },
140
140
  )), false);
141
141
  });
142
142
 
143
143
  test("rejects an origin that does not match the external request host", async () => {
144
144
  const { isApiRequestAllowed } = await loadSubject();
145
- const request = new Request("http://localhost:30141/api/test", {
145
+ const request = new Request("http://localhost:30143/api/test", {
146
146
  method: "POST",
147
147
  headers: {
148
- host: "192.168.32.7:30141",
148
+ host: "192.168.32.7:30143",
149
149
  origin: "http://attacker.example",
150
150
  "sec-fetch-site": "same-site",
151
151
  },
@@ -155,11 +155,11 @@ test("rejects an origin that does not match the external request host", async ()
155
155
 
156
156
  test("rejects DNS rebinding even when browser headers say same-origin", async () => {
157
157
  const { isApiRequestAllowed } = await loadSubject();
158
- const request = new Request("http://localhost:30141/api/skills/install", {
158
+ const request = new Request("http://localhost:30143/api/skills/install", {
159
159
  method: "POST",
160
160
  headers: {
161
- host: "attacker.example:30141",
162
- origin: "http://attacker.example:30141",
161
+ host: "attacker.example:30143",
162
+ origin: "http://attacker.example:30143",
163
163
  "sec-fetch-site": "same-origin",
164
164
  "content-type": "application/json",
165
165
  },
@@ -169,21 +169,21 @@ test("rejects DNS rebinding even when browser headers say same-origin", async ()
169
169
 
170
170
  test("rejects missing, malformed, and unconfigured Host headers", async () => {
171
171
  const { isApiRequestAllowed } = await loadSubject();
172
- assert.equal(isApiRequestAllowed(new Request("http://localhost:30141/api/test")), false);
173
- assert.equal(isApiRequestAllowed(new Request("http://localhost:30141/api/test", {
174
- headers: { host: "localhost@attacker.example:30141" },
172
+ assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test")), false);
173
+ assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
174
+ headers: { host: "localhost@attacker.example:30143" },
175
175
  })), false);
176
- assert.equal(isApiRequestAllowed(new Request("http://localhost:30141/api/test", {
177
- headers: { host: "pi-web.internal:30141" },
176
+ assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
177
+ headers: { host: "pi-web.internal:30143" },
178
178
  })), false);
179
179
  });
180
180
 
181
181
  test("allows same-origin requests when Chromium strips the port from Origin", async () => {
182
182
  const { isApiRequestAllowed } = await loadSubject();
183
183
  // Loopback IP with the port stripped from Origin (Chromium 150+ behavior).
184
- const loopback = new Request("http://127.0.0.1:30141/api/agent/running", {
184
+ const loopback = new Request("http://127.0.0.1:30143/api/agent/running", {
185
185
  headers: {
186
- host: "127.0.0.1:30141",
186
+ host: "127.0.0.1:30143",
187
187
  origin: "http://127.0.0.1",
188
188
  "sec-fetch-site": "same-origin",
189
189
  },
@@ -191,10 +191,10 @@ test("allows same-origin requests when Chromium strips the port from Origin", as
191
191
  assert.equal(isApiRequestAllowed(loopback), true);
192
192
 
193
193
  // LAN IP with the port stripped from Origin.
194
- const lan = new Request("http://192.168.32.7:30141/api/test", {
194
+ const lan = new Request("http://192.168.32.7:30143/api/test", {
195
195
  method: "POST",
196
196
  headers: {
197
- host: "192.168.32.7:30141",
197
+ host: "192.168.32.7:30143",
198
198
  origin: "http://192.168.32.7",
199
199
  "sec-fetch-site": "same-origin",
200
200
  "content-type": "application/json",
@@ -203,10 +203,10 @@ test("allows same-origin requests when Chromium strips the port from Origin", as
203
203
  assert.equal(isApiRequestAllowed(lan), true);
204
204
 
205
205
  // Loopback name with the port stripped from Origin.
206
- const named = new Request("http://localhost:30141/api/test", {
206
+ const named = new Request("http://localhost:30143/api/test", {
207
207
  method: "POST",
208
208
  headers: {
209
- host: "localhost:30141",
209
+ host: "localhost:30143",
210
210
  origin: "http://localhost",
211
211
  "sec-fetch-site": "same-origin",
212
212
  "content-type": "application/json",
@@ -219,9 +219,9 @@ test("still rejects when Origin hostname differs from Host even with port stripp
219
219
  const { isApiRequestAllowed } = await loadSubject();
220
220
  // Cross-loopback-name origin should not slip through just because both
221
221
  // sides happen to be loopback addresses.
222
- const crossName = new Request("http://localhost:30141/api/test", {
222
+ const crossName = new Request("http://localhost:30143/api/test", {
223
223
  headers: {
224
- host: "localhost:30141",
224
+ host: "localhost:30143",
225
225
  origin: "http://127.0.0.1",
226
226
  "sec-fetch-site": "same-origin",
227
227
  },
@@ -230,10 +230,10 @@ test("still rejects when Origin hostname differs from Host even with port stripp
230
230
 
231
231
  // Attacker host matching its own Origin must still be blocked by the
232
232
  // Host allowlist, not the Origin comparison.
233
- const dnsRebind = new Request("http://localhost:30141/api/skills/install", {
233
+ const dnsRebind = new Request("http://localhost:30143/api/skills/install", {
234
234
  method: "POST",
235
235
  headers: {
236
- host: "attacker.example:30141",
236
+ host: "attacker.example:30143",
237
237
  origin: "http://attacker.example",
238
238
  "sec-fetch-site": "same-origin",
239
239
  "content-type": "application/json",
@@ -104,7 +104,7 @@ export function isApiRequestOriginAllowed(request: Request): boolean {
104
104
 
105
105
  // Chromium 150+ strips the port from the Origin header for same-origin
106
106
  // requests on non-default ports. Strict canonical-origin comparison would
107
- // therefore reject those legitimate requests ("http://127.0.0.1:30141"
107
+ // therefore reject those legitimate requests ("http://127.0.0.1:30143"
108
108
  // vs. the browser-sent "http://127.0.0.1"). The Host header is the
109
109
  // authoritative source for where the request actually went, so accept any
110
110
  // Origin whose hostname matches it. Hostnames are case-insensitive, which
package/lib/sqlite-db.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
+ import { mkdirSync } from "fs";
2
3
  import { homedir } from "os";
3
4
  import { join, normalize } from "path";
4
5
 
@@ -40,6 +41,10 @@ export function setDbForTesting(db: DatabaseSync): void {
40
41
  */
41
42
  export function getDb(): DatabaseSync {
42
43
  if (globalThis.__piWebDb) return globalThis.__piWebDb;
44
+ // SQLite 打不开不存在的父目录(unable to open database file)。全新机器上
45
+ // ~/.pi/agent 尚未被 SDK 创建,惰性打开也会炸——与 lib/yjs-room-server.mjs
46
+ // 的顶层打开是同类隐患,打开前必须确保目录存在。
47
+ mkdirSync(getAgentDir(), { recursive: true });
43
48
  const db = new DatabaseSync(dbPath());
44
49
  db.exec("PRAGMA journal_mode = WAL;");
45
50
  db.exec("PRAGMA foreign_keys = ON;");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baique/pi-web-sky",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "description": "Web UI for the pi coding agent — Sky skin edition (fork of @agegr/pi-web / earendil-labs pi-web, MIT)",
5
5
  "homepage": "https://github.com/baique/pi-web-sky#readme",
6
6
  "repository": {