@rubytech/create-maxy-code 0.1.544 → 0.1.546
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.
- package/package.json +1 -1
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js +11 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +39 -0
- package/payload/platform/lib/mcp-spawn-tee/src/index.ts +12 -0
- package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/admin/skills/channel-instruction/SKILL.md +26 -12
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +9 -0
- package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +2 -0
- package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/google/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js +80 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.js +32 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.d.ts +67 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.js +88 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts +2 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +59 -54
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
- package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +11 -0
- package/payload/platform/services/claude-session-manager/dist/channel-mcp.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/channel-mcp.js +16 -2
- package/payload/platform/services/claude-session-manager/dist/channel-mcp.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.d.ts +34 -0
- package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.js +41 -0
- package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +26 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +11 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js +16 -2
- package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +17 -2
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +16 -2
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts +1 -0
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.js +47 -0
- package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +8 -14
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +13 -19
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.js +3 -10
- package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +60 -0
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +9 -14
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +13 -19
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +5 -10
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +91 -9
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +155 -17
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +83 -19
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +23 -3
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js +19 -2
- package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
- package/payload/server/server.js +42 -43
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { writeSnapshot, MAX_ATTEMPTS, RETRY_START_DEADLINE_MS, SNAPSHOT_TABLE_DDL, SNAPSHOT_UPSERT_SQL, } from "../availability-snapshot.js";
|
|
3
|
+
/** A D1Client that records every statement and rejects the first `failFirstN`. */
|
|
4
|
+
function fakeClient(failFirstN = 0) {
|
|
5
|
+
const issued = [];
|
|
6
|
+
let failuresLeft = failFirstN;
|
|
7
|
+
return {
|
|
8
|
+
issued,
|
|
9
|
+
async query(dbName, sql, params = []) {
|
|
10
|
+
issued.push({ dbName, sql, params });
|
|
11
|
+
if (failuresLeft > 0) {
|
|
12
|
+
failuresLeft--;
|
|
13
|
+
throw new Error('scheduling: d1 query failed: 500\n{"errors":[{"code":7500}]}');
|
|
14
|
+
}
|
|
15
|
+
return [];
|
|
16
|
+
},
|
|
17
|
+
get statements() {
|
|
18
|
+
return issued.length;
|
|
19
|
+
},
|
|
20
|
+
get elapsedMs() {
|
|
21
|
+
return 42;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
describe("writeSnapshot", () => {
|
|
26
|
+
it("issues the schema statement then the upsert, and reports one attempt", async () => {
|
|
27
|
+
const client = fakeClient();
|
|
28
|
+
const result = await writeSnapshot(client, "maxy-bookings", '{"slots":[]}', "2026-08-02T06:00:00.000Z");
|
|
29
|
+
expect(result.ok).toBe(true);
|
|
30
|
+
expect(result.attempts).toBe(1);
|
|
31
|
+
expect(result.elapsedMs).toBe(42);
|
|
32
|
+
expect(client.issued).toHaveLength(2);
|
|
33
|
+
expect(client.issued[0].sql).toBe(SNAPSHOT_TABLE_DDL);
|
|
34
|
+
expect(client.issued[1].sql).toBe(SNAPSHOT_UPSERT_SQL);
|
|
35
|
+
expect(client.issued[0].dbName).toBe("maxy-bookings");
|
|
36
|
+
});
|
|
37
|
+
it("binds the payload and updatedAt rather than interpolating them", async () => {
|
|
38
|
+
const client = fakeClient();
|
|
39
|
+
// A quote is the exact character the retired string-escaping path hand-handled.
|
|
40
|
+
const payload = `{"note":"it's fine"}`;
|
|
41
|
+
await writeSnapshot(client, "maxy-bookings", payload, "2026-08-02T06:00:00.000Z");
|
|
42
|
+
expect(client.issued[1].params).toEqual([payload, "2026-08-02T06:00:00.000Z"]);
|
|
43
|
+
expect(client.issued[1].sql).not.toContain("it's fine");
|
|
44
|
+
expect(client.issued[1].sql).toContain("?1");
|
|
45
|
+
expect(client.issued[1].sql).toContain("?2");
|
|
46
|
+
});
|
|
47
|
+
it("retries the whole pair once and succeeds on the second attempt", async () => {
|
|
48
|
+
const client = fakeClient(1);
|
|
49
|
+
const result = await writeSnapshot(client, "maxy-bookings", '{"slots":[]}', "2026-08-02T06:00:00.000Z");
|
|
50
|
+
expect(result.ok).toBe(true);
|
|
51
|
+
expect(result.attempts).toBe(2);
|
|
52
|
+
expect(result.error).toBeUndefined();
|
|
53
|
+
// Attempt 1 issued only the DDL before it threw; attempt 2 issued both.
|
|
54
|
+
expect(client.issued).toHaveLength(3);
|
|
55
|
+
expect(client.issued[1].sql).toBe(SNAPSHOT_TABLE_DDL);
|
|
56
|
+
expect(client.issued[2].sql).toBe(SNAPSHOT_UPSERT_SQL);
|
|
57
|
+
});
|
|
58
|
+
it("refuses the retry when the first attempt ran past the deadline", async () => {
|
|
59
|
+
const client = fakeClient(Number.MAX_SAFE_INTEGER);
|
|
60
|
+
// A clock that jumps the deadline between the start reading and the check.
|
|
61
|
+
// Without the guard a second attempt would start here and could hold the
|
|
62
|
+
// per-account lock into the next 300 s tick.
|
|
63
|
+
const readings = [0, RETRY_START_DEADLINE_MS];
|
|
64
|
+
let i = 0;
|
|
65
|
+
const now = () => readings[Math.min(i++, readings.length - 1)];
|
|
66
|
+
const result = await writeSnapshot(client, "maxy-bookings", '{"slots":[]}', "2026-08-02T06:00:00.000Z", now);
|
|
67
|
+
expect(result.ok).toBe(false);
|
|
68
|
+
expect(result.attempts).toBe(1);
|
|
69
|
+
expect(client.issued).toHaveLength(1);
|
|
70
|
+
});
|
|
71
|
+
it("gives up after MAX_ATTEMPTS and carries the last error", async () => {
|
|
72
|
+
const client = fakeClient(Number.MAX_SAFE_INTEGER);
|
|
73
|
+
const result = await writeSnapshot(client, "maxy-bookings", '{"slots":[]}', "2026-08-02T06:00:00.000Z");
|
|
74
|
+
expect(result.ok).toBe(false);
|
|
75
|
+
expect(result.attempts).toBe(MAX_ATTEMPTS);
|
|
76
|
+
expect(result.error).toContain("d1 query failed: 500");
|
|
77
|
+
expect(client.issued).toHaveLength(MAX_ATTEMPTS);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=availability-snapshot.test.js.map
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability-snapshot.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/availability-snapshot.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-availability-logging.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/publish-availability-logging.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* The dispatcher's lines are its only signal, and the server spawns it with
|
|
6
|
+
* `stdio: 'inherit'`, so an undated line lands in server.log beside the
|
|
7
|
+
* server's own dated ones and cannot be attributed to a tick without counting
|
|
8
|
+
* neighbouring lines.
|
|
9
|
+
*
|
|
10
|
+
* Asserted against the source rather than by running the script: `main()`
|
|
11
|
+
* executes on import, so there is no way to import this module and observe its
|
|
12
|
+
* output without also performing a publish.
|
|
13
|
+
*/
|
|
14
|
+
const SOURCE = readFileSync(resolve(__dirname, "../../scripts/publish-availability.ts"), "utf-8");
|
|
15
|
+
/** The source with comments stripped, so a comment describing the retired
|
|
16
|
+
* approach is never mistaken for the approach still being in use. */
|
|
17
|
+
const CODE = SOURCE.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
|
|
18
|
+
describe("publish-availability logging", () => {
|
|
19
|
+
it("writes to stderr in exactly one place, the dated log helper", () => {
|
|
20
|
+
// One call site means every emission is dated by construction: there is no
|
|
21
|
+
// second path to stderr for a future line to take.
|
|
22
|
+
expect(CODE.match(/console\.error\(/g) ?? []).toHaveLength(1);
|
|
23
|
+
expect(CODE).toContain("function log(line: string): void {\n console.error(`${new Date().toISOString()} ${line}`);");
|
|
24
|
+
});
|
|
25
|
+
it("no longer shells a CLI or stages a temp SQL file", () => {
|
|
26
|
+
expect(CODE).not.toMatch(/\bspawnSync\s*\(/);
|
|
27
|
+
expect(CODE).not.toMatch(/\bmkdtempSync\s*\(|\brmSync\s*\(|\btmpdir\s*\(/);
|
|
28
|
+
expect(CODE).not.toMatch(/["']wrangler["']/);
|
|
29
|
+
expect(CODE).not.toMatch(/node:child_process/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=publish-availability-logging.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-availability-logging.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/publish-availability-logging.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,MAAM,GAAG,YAAY,CACzB,OAAO,CAAC,SAAS,EAAE,uCAAuC,CAAC,EAC3D,OAAO,CACR,CAAC;AAEF;sEACsE;AACtE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAElF,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,2EAA2E;QAC3E,mDAAmD;QACnD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CACpB,6FAA6F,CAC9F,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The booking page's availability snapshot: its schema statement, its upsert,
|
|
3
|
+
* and the bounded write that issues both.
|
|
4
|
+
*
|
|
5
|
+
* Task 2349 — this write used to be `npx wrangler d1 execute` run through
|
|
6
|
+
* spawnSync from the publish dispatcher. That spawn's own 60 s timeout killed a
|
|
7
|
+
* slow write with SIGTERM, and the failure branch logged only `status` and
|
|
8
|
+
* `signal` while discarding the stderr it had captured, so a killed write could
|
|
9
|
+
* not be told from a rejected one. Task 1805 had already replaced the same
|
|
10
|
+
* spawn on the reconcile loop with the D1 HTTP client and scoped this caller
|
|
11
|
+
* out as "not on this loop"; this completes that migration.
|
|
12
|
+
*
|
|
13
|
+
* The payload is bound, never interpolated. The retired path hand-escaped
|
|
14
|
+
* single quotes into the statement text, which is a correctness burden this
|
|
15
|
+
* transport removes rather than reimplements.
|
|
16
|
+
*/
|
|
17
|
+
import type { D1Client } from "./d1-command.js";
|
|
18
|
+
/** Attempts one write gets. */
|
|
19
|
+
export declare const MAX_ATTEMPTS = 2;
|
|
20
|
+
/**
|
|
21
|
+
* How late a failed attempt may be and still earn a retry.
|
|
22
|
+
*
|
|
23
|
+
* The ceiling here is not the obvious one. `D1_TIMEOUT_MS` binds a single
|
|
24
|
+
* REQUEST, not an attempt, and one attempt issues up to three of them (name
|
|
25
|
+
* lookup, schema, upsert). Two unguarded attempts therefore reach ~300 s, which
|
|
26
|
+
* IS the publish interval: the run would still hold the per-account PID lock
|
|
27
|
+
* when the next tick spawns, that tick would skip with reason=lock-held, and a
|
|
28
|
+
* retry meant to save one interval would instead have cost two.
|
|
29
|
+
*
|
|
30
|
+
* A second attempt is two requests once the name lookup has cached, so 120 s.
|
|
31
|
+
* Refusing to start one after this point holds the whole write under ~240 s and
|
|
32
|
+
* guarantees the lock is released before the next tick. The realistic failures
|
|
33
|
+
* sit nowhere near it — a timing-out statement fails its attempt at ~60 s — so
|
|
34
|
+
* this forfeits no retry that was going to help.
|
|
35
|
+
*/
|
|
36
|
+
export declare const RETRY_START_DEADLINE_MS = 120000;
|
|
37
|
+
/**
|
|
38
|
+
* Self-healing schema. A booking site provisioned before the snapshot feature
|
|
39
|
+
* has a `bookings` table but no `availability_snapshot`; issuing this first
|
|
40
|
+
* means the table appears on the next publish rather than waiting for a skill
|
|
41
|
+
* re-run.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SNAPSHOT_TABLE_DDL = "CREATE TABLE IF NOT EXISTS availability_snapshot (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n payload TEXT,\n updatedAt TEXT\n)";
|
|
44
|
+
/** One row, id 1, replaced in place on every publish. */
|
|
45
|
+
export declare const SNAPSHOT_UPSERT_SQL = "INSERT INTO availability_snapshot (id, payload, updatedAt)\nVALUES (1, ?1, ?2)\nON CONFLICT(id) DO UPDATE SET payload=excluded.payload, updatedAt=excluded.updatedAt";
|
|
46
|
+
export interface SnapshotWriteResult {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
/** Attempts actually made. Never a retry that the deadline refused. */
|
|
49
|
+
attempts: number;
|
|
50
|
+
/** Total wall time spent in D1 across every attempt, the client's own figure. */
|
|
51
|
+
elapsedMs: number;
|
|
52
|
+
/** The last failure's message. Absent when ok. */
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Write the snapshot, retrying the whole pair once while there is time for it.
|
|
57
|
+
*
|
|
58
|
+
* Both statements retry together rather than only the upsert: the DDL is
|
|
59
|
+
* IF NOT EXISTS so re-issuing it costs nothing, and one client across both
|
|
60
|
+
* attempts reuses its cached database-name lookup instead of paying for it
|
|
61
|
+
* twice.
|
|
62
|
+
*
|
|
63
|
+
* Returns a result rather than throwing so the caller's success and failure log
|
|
64
|
+
* lines read straight off it, carrying the same fields either way.
|
|
65
|
+
*/
|
|
66
|
+
export declare function writeSnapshot(client: D1Client, dbName: string, payload: string, updatedAt: string, now?: () => number): Promise<SnapshotWriteResult>;
|
|
67
|
+
//# sourceMappingURL=availability-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability-snapshot.d.ts","sourceRoot":"","sources":["../../src/lib/availability-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,+BAA+B;AAC/B,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,gJAI7B,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,yKAEqD,CAAC;AAEtF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EAIjB,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAmB9B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The booking page's availability snapshot: its schema statement, its upsert,
|
|
3
|
+
* and the bounded write that issues both.
|
|
4
|
+
*
|
|
5
|
+
* Task 2349 — this write used to be `npx wrangler d1 execute` run through
|
|
6
|
+
* spawnSync from the publish dispatcher. That spawn's own 60 s timeout killed a
|
|
7
|
+
* slow write with SIGTERM, and the failure branch logged only `status` and
|
|
8
|
+
* `signal` while discarding the stderr it had captured, so a killed write could
|
|
9
|
+
* not be told from a rejected one. Task 1805 had already replaced the same
|
|
10
|
+
* spawn on the reconcile loop with the D1 HTTP client and scoped this caller
|
|
11
|
+
* out as "not on this loop"; this completes that migration.
|
|
12
|
+
*
|
|
13
|
+
* The payload is bound, never interpolated. The retired path hand-escaped
|
|
14
|
+
* single quotes into the statement text, which is a correctness burden this
|
|
15
|
+
* transport removes rather than reimplements.
|
|
16
|
+
*/
|
|
17
|
+
/** Attempts one write gets. */
|
|
18
|
+
export const MAX_ATTEMPTS = 2;
|
|
19
|
+
/**
|
|
20
|
+
* How late a failed attempt may be and still earn a retry.
|
|
21
|
+
*
|
|
22
|
+
* The ceiling here is not the obvious one. `D1_TIMEOUT_MS` binds a single
|
|
23
|
+
* REQUEST, not an attempt, and one attempt issues up to three of them (name
|
|
24
|
+
* lookup, schema, upsert). Two unguarded attempts therefore reach ~300 s, which
|
|
25
|
+
* IS the publish interval: the run would still hold the per-account PID lock
|
|
26
|
+
* when the next tick spawns, that tick would skip with reason=lock-held, and a
|
|
27
|
+
* retry meant to save one interval would instead have cost two.
|
|
28
|
+
*
|
|
29
|
+
* A second attempt is two requests once the name lookup has cached, so 120 s.
|
|
30
|
+
* Refusing to start one after this point holds the whole write under ~240 s and
|
|
31
|
+
* guarantees the lock is released before the next tick. The realistic failures
|
|
32
|
+
* sit nowhere near it — a timing-out statement fails its attempt at ~60 s — so
|
|
33
|
+
* this forfeits no retry that was going to help.
|
|
34
|
+
*/
|
|
35
|
+
export const RETRY_START_DEADLINE_MS = 120_000;
|
|
36
|
+
/**
|
|
37
|
+
* Self-healing schema. A booking site provisioned before the snapshot feature
|
|
38
|
+
* has a `bookings` table but no `availability_snapshot`; issuing this first
|
|
39
|
+
* means the table appears on the next publish rather than waiting for a skill
|
|
40
|
+
* re-run.
|
|
41
|
+
*/
|
|
42
|
+
export const SNAPSHOT_TABLE_DDL = `CREATE TABLE IF NOT EXISTS availability_snapshot (
|
|
43
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
44
|
+
payload TEXT,
|
|
45
|
+
updatedAt TEXT
|
|
46
|
+
)`;
|
|
47
|
+
/** One row, id 1, replaced in place on every publish. */
|
|
48
|
+
export const SNAPSHOT_UPSERT_SQL = `INSERT INTO availability_snapshot (id, payload, updatedAt)
|
|
49
|
+
VALUES (1, ?1, ?2)
|
|
50
|
+
ON CONFLICT(id) DO UPDATE SET payload=excluded.payload, updatedAt=excluded.updatedAt`;
|
|
51
|
+
/**
|
|
52
|
+
* Write the snapshot, retrying the whole pair once while there is time for it.
|
|
53
|
+
*
|
|
54
|
+
* Both statements retry together rather than only the upsert: the DDL is
|
|
55
|
+
* IF NOT EXISTS so re-issuing it costs nothing, and one client across both
|
|
56
|
+
* attempts reuses its cached database-name lookup instead of paying for it
|
|
57
|
+
* twice.
|
|
58
|
+
*
|
|
59
|
+
* Returns a result rather than throwing so the caller's success and failure log
|
|
60
|
+
* lines read straight off it, carrying the same fields either way.
|
|
61
|
+
*/
|
|
62
|
+
export async function writeSnapshot(client, dbName, payload, updatedAt,
|
|
63
|
+
// Injectable so the retry deadline is reachable from a test rather than only
|
|
64
|
+
// from a real two-minute wall-clock wait. The same seam d1-command.ts gives
|
|
65
|
+
// its transport.
|
|
66
|
+
now = Date.now) {
|
|
67
|
+
const startedAt = now();
|
|
68
|
+
let lastError = "";
|
|
69
|
+
let attempts = 0;
|
|
70
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
71
|
+
attempts = attempt;
|
|
72
|
+
try {
|
|
73
|
+
await client.query(dbName, SNAPSHOT_TABLE_DDL);
|
|
74
|
+
await client.query(dbName, SNAPSHOT_UPSERT_SQL, [payload, updatedAt]);
|
|
75
|
+
return { ok: true, attempts, elapsedMs: client.elapsedMs };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
79
|
+
// Too late to start another one. Reported as the attempts actually made,
|
|
80
|
+
// never as MAX_ATTEMPTS, so the log line cannot claim a retry that was
|
|
81
|
+
// refused.
|
|
82
|
+
if (now() - startedAt >= RETRY_START_DEADLINE_MS)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ok: false, attempts, elapsedMs: client.elapsedMs, error: lastError };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=availability-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability-snapshot.js","sourceRoot":"","sources":["../../src/lib/availability-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,+BAA+B;AAC/B,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;EAIhC,CAAC;AAEH,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG;;qFAEkD,CAAC;AAYtF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAgB,EAChB,MAAc,EACd,OAAe,EACf,SAAiB;AACjB,6EAA6E;AAC7E,4EAA4E;AAC5E,iBAAiB;AACjB,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;IACxB,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,QAAQ,GAAG,OAAO,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;YAC/C,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;YACtE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,yEAAyE;YACzE,uEAAuE;YACvE,WAAW;YACX,IAAI,GAAG,EAAE,GAAG,SAAS,IAAI,uBAAuB;gBAAE,MAAM;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAChF,CAAC"}
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* overlapping runs. Each run records the outcome to a local last-publish.json so
|
|
19
19
|
* the server's audit tick can report snapshot age independent of this job.
|
|
20
20
|
*
|
|
21
|
-
* Observability — one [calendar-availability] op=publish line per run
|
|
21
|
+
* Observability — one dated [calendar-availability] op=publish line per run,
|
|
22
|
+
* carrying d1Ms (time in D1, the bound is 60 s) and attempt (1 or 2).
|
|
22
23
|
*
|
|
23
24
|
* PLATFORM_ROOT=... MAXY_UI_INTERNAL_PORT=... PUBLISH_ACCOUNT_DIR=... \
|
|
24
25
|
* PUBLISH_ACCOUNT_ID=... node dist/scripts/publish-availability.js
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-availability.d.ts","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"publish-availability.d.ts","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
|
@@ -18,17 +18,37 @@
|
|
|
18
18
|
* overlapping runs. Each run records the outcome to a local last-publish.json so
|
|
19
19
|
* the server's audit tick can report snapshot age independent of this job.
|
|
20
20
|
*
|
|
21
|
-
* Observability — one [calendar-availability] op=publish line per run
|
|
21
|
+
* Observability — one dated [calendar-availability] op=publish line per run,
|
|
22
|
+
* carrying d1Ms (time in D1, the bound is 60 s) and attempt (1 or 2).
|
|
22
23
|
*
|
|
23
24
|
* PLATFORM_ROOT=... MAXY_UI_INTERNAL_PORT=... PUBLISH_ACCOUNT_DIR=... \
|
|
24
25
|
* PUBLISH_ACCOUNT_ID=... node dist/scripts/publish-availability.js
|
|
25
26
|
*/
|
|
26
|
-
import {
|
|
27
|
-
import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
27
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from "node:fs";
|
|
28
28
|
import { resolve, join, basename } from "node:path";
|
|
29
|
-
import { homedir
|
|
29
|
+
import { homedir } from "node:os";
|
|
30
30
|
import { resolveHouseScopedToken } from "../lib/house-scoped-token.js";
|
|
31
31
|
import { buildFreeBusyUrl } from "../lib/free-busy-url.js";
|
|
32
|
+
import { createD1Client } from "../lib/d1-command.js";
|
|
33
|
+
import { writeSnapshot } from "../lib/availability-snapshot.js";
|
|
34
|
+
/** Every line this dispatcher emits, dated.
|
|
35
|
+
*
|
|
36
|
+
* Task 2349 — the server spawns this script with `stdio: 'inherit'`, so its
|
|
37
|
+
* lines land in server.log beside the server's own timestamped audit lines
|
|
38
|
+
* while carrying no timestamp of their own. Dating the 2026-08-02 publish
|
|
39
|
+
* failure meant bracketing it against its neighbours by line number. */
|
|
40
|
+
function log(line) {
|
|
41
|
+
console.error(`${new Date().toISOString()} ${line}`);
|
|
42
|
+
}
|
|
43
|
+
/** One log-safe line from a failure message.
|
|
44
|
+
*
|
|
45
|
+
* The HEAD, not the tail. The D1 client's message opens with its context and
|
|
46
|
+
* the HTTP status (`scheduling: d1 query failed: 403 …`), so the first
|
|
47
|
+
* characters are the diagnosis and a long response body is the part worth
|
|
48
|
+
* dropping. Newlines and quotes collapse so the field stays on one line. */
|
|
49
|
+
function errField(message) {
|
|
50
|
+
return (message ?? "").replace(/[\n\r"]+/g, " ").slice(0, 200);
|
|
51
|
+
}
|
|
32
52
|
function resolveConfigDir() {
|
|
33
53
|
const platformRoot = process.env.PLATFORM_ROOT;
|
|
34
54
|
if (!platformRoot)
|
|
@@ -121,13 +141,13 @@ function utcMidnight(now, addDays) {
|
|
|
121
141
|
}
|
|
122
142
|
async function main() {
|
|
123
143
|
if (!acquireLock()) {
|
|
124
|
-
|
|
144
|
+
log("[calendar-availability] op=publish result=skipped reason=lock-held");
|
|
125
145
|
return;
|
|
126
146
|
}
|
|
127
147
|
try {
|
|
128
148
|
const accountDir = process.env.PUBLISH_ACCOUNT_DIR;
|
|
129
149
|
if (!accountDir) {
|
|
130
|
-
|
|
150
|
+
log("[calendar-availability] op=publish result=skipped reason=no-account");
|
|
131
151
|
return;
|
|
132
152
|
}
|
|
133
153
|
const accountId = process.env.PUBLISH_ACCOUNT_ID ?? basename(accountDir);
|
|
@@ -141,44 +161,53 @@ async function main() {
|
|
|
141
161
|
avail = JSON.parse(readFileSync(availPath, "utf-8"));
|
|
142
162
|
}
|
|
143
163
|
catch {
|
|
144
|
-
|
|
164
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=error reason=bad-availability-json`);
|
|
145
165
|
writeLastPublish(accountDir, "error", null, readPriorSuccessAt(accountDir));
|
|
146
166
|
return;
|
|
147
167
|
}
|
|
148
168
|
const dbName = avail.bookingDbName;
|
|
149
169
|
if (!dbName) {
|
|
150
|
-
|
|
170
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=no-booking-db`);
|
|
151
171
|
return;
|
|
152
172
|
}
|
|
153
173
|
// Task 1631 — house code uses the house-only Cloudflare credential, never a
|
|
154
174
|
// per-account token. The booking DB is still targeted by its per-account name.
|
|
155
175
|
const platformRoot = process.env.PLATFORM_ROOT;
|
|
156
176
|
if (!platformRoot) {
|
|
157
|
-
|
|
177
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=no-platform-root`);
|
|
158
178
|
return;
|
|
159
179
|
}
|
|
160
180
|
const houseEnvPath = join(platformRoot, "config", "cloudflare-house.env");
|
|
161
181
|
const houseCred = readEnvFile(houseEnvPath);
|
|
162
182
|
const token = houseCred.CLOUDFLARE_API_TOKEN;
|
|
163
183
|
if (!token) {
|
|
164
|
-
|
|
184
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=no-house-cloudflare-credential`);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Task 1805/2349 — the D1 endpoint is addressed per Cloudflare account, so
|
|
188
|
+
// the id is required rather than inferred. cf-token.sh already refuses to
|
|
189
|
+
// resolve any scope without it, so no install that publishes today can
|
|
190
|
+
// reach here lacking one; an absent id is reported rather than guessed.
|
|
191
|
+
const houseAccountId = houseCred.CLOUDFLARE_ACCOUNT_ID;
|
|
192
|
+
if (!houseAccountId) {
|
|
193
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=no-house-cloudflare-account-id`);
|
|
165
194
|
return;
|
|
166
195
|
}
|
|
167
196
|
// Task 1670 — the house credential is a minter; mint (then reuse) a D1-only
|
|
168
|
-
// scoped token from it via cf-token.sh and
|
|
169
|
-
// minter directly (a minter returns 10000
|
|
197
|
+
// scoped token from it via cf-token.sh and authenticate on THAT, never the
|
|
198
|
+
// minter directly (a minter returns 10000 against the D1 surface).
|
|
170
199
|
const cfTokenSh = join(platformRoot, "plugins/cloudflare/bin/cf-token.sh");
|
|
171
200
|
let scopedToken;
|
|
172
201
|
try {
|
|
173
202
|
scopedToken = resolveHouseScopedToken("calendar-d1", houseEnvPath, cfTokenSh);
|
|
174
203
|
}
|
|
175
204
|
catch (err) {
|
|
176
|
-
|
|
205
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=scoped-token-mint-failed err="${err.message}"`);
|
|
177
206
|
return;
|
|
178
207
|
}
|
|
179
208
|
const port = process.env.MAXY_UI_INTERNAL_PORT;
|
|
180
209
|
if (!port) {
|
|
181
|
-
|
|
210
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=error reason=no-internal-port`);
|
|
182
211
|
writeLastPublish(accountDir, "error", null, readPriorSuccessAt(accountDir));
|
|
183
212
|
return;
|
|
184
213
|
}
|
|
@@ -203,7 +232,7 @@ async function main() {
|
|
|
203
232
|
}
|
|
204
233
|
catch (err) {
|
|
205
234
|
// Device/local endpoint unreachable — leave the last edge snapshot intact.
|
|
206
|
-
|
|
235
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped-unreachable err="${err.message}"`);
|
|
207
236
|
writeLastPublish(accountDir, "skipped-unreachable", null, readPriorSuccessAt(accountDir));
|
|
208
237
|
return;
|
|
209
238
|
}
|
|
@@ -216,48 +245,24 @@ async function main() {
|
|
|
216
245
|
slotCount = parsed.slots.length;
|
|
217
246
|
}
|
|
218
247
|
catch (err) {
|
|
219
|
-
|
|
248
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=skipped reason=bad-payload err="${err.message}"`);
|
|
220
249
|
writeLastPublish(accountDir, "skipped", null, readPriorSuccessAt(accountDir));
|
|
221
250
|
return;
|
|
222
251
|
}
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
INSERT INTO availability_snapshot (id, payload, updatedAt)
|
|
235
|
-
VALUES (1, '${esc}', '${nowIso}')
|
|
236
|
-
ON CONFLICT(id) DO UPDATE SET payload=excluded.payload, updatedAt=excluded.updatedAt;`;
|
|
237
|
-
const workDir = mkdtempSync(join(tmpdir(), "publish-availability-"));
|
|
238
|
-
const sqlFile = join(workDir, "upsert.sql");
|
|
239
|
-
try {
|
|
240
|
-
writeFileSync(sqlFile, sql);
|
|
241
|
-
const wranglerEnv = {
|
|
242
|
-
CLOUDFLARE_API_TOKEN: scopedToken,
|
|
243
|
-
...(houseCred.CLOUDFLARE_ACCOUNT_ID ? { CLOUDFLARE_ACCOUNT_ID: houseCred.CLOUDFLARE_ACCOUNT_ID } : {}),
|
|
244
|
-
};
|
|
245
|
-
const proc = spawnSync("npx", ["wrangler", "d1", "execute", dbName, "--remote", "--file", sqlFile], {
|
|
246
|
-
env: { ...process.env, ...wranglerEnv },
|
|
247
|
-
encoding: "utf-8",
|
|
248
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
249
|
-
timeout: 60_000,
|
|
250
|
-
});
|
|
251
|
-
if (proc.status !== 0) {
|
|
252
|
-
console.error(`[calendar-availability] op=publish accountId=${accountId} result=error reason=d1-write-failed status=${proc.status}${proc.signal ? ` signal=${proc.signal}` : ""}`);
|
|
253
|
-
writeLastPublish(accountDir, "error", slotCount, readPriorSuccessAt(accountDir));
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
finally {
|
|
258
|
-
rmSync(workDir, { recursive: true, force: true });
|
|
252
|
+
// Task 2349 — the write goes over the D1 HTTP API (Task 1805's transport),
|
|
253
|
+
// not `npx wrangler d1 execute` through spawnSync. The client carries the
|
|
254
|
+
// spawn's 60 s bound as a request timeout, and its failures name the HTTP
|
|
255
|
+
// status and the response body, which the spawn's discarded stderr did not.
|
|
256
|
+
const client = createD1Client({ accountId: houseAccountId, token: scopedToken });
|
|
257
|
+
const write = await writeSnapshot(client, dbName, payload, nowIso);
|
|
258
|
+
if (!write.ok) {
|
|
259
|
+
log(`[calendar-availability] op=publish accountId=${accountId} result=error reason=d1-write-failed ` +
|
|
260
|
+
`d1Ms=${write.elapsedMs} attempt=${write.attempts} err="${errField(write.error)}"`);
|
|
261
|
+
writeLastPublish(accountDir, "error", slotCount, readPriorSuccessAt(accountDir));
|
|
262
|
+
return;
|
|
259
263
|
}
|
|
260
|
-
|
|
264
|
+
log(`[calendar-availability] op=publish accountId=${accountId} slots=${slotCount} result=success ` +
|
|
265
|
+
`d1Ms=${write.elapsedMs} attempt=${write.attempts}`);
|
|
261
266
|
writeLastPublish(accountDir, "success", slotCount, nowIso);
|
|
262
267
|
}
|
|
263
268
|
finally {
|
|
@@ -265,7 +270,7 @@ ON CONFLICT(id) DO UPDATE SET payload=excluded.payload, updatedAt=excluded.updat
|
|
|
265
270
|
}
|
|
266
271
|
}
|
|
267
272
|
main().catch((err) => {
|
|
268
|
-
|
|
273
|
+
log(`[calendar-availability] op=publish result=error reason=fatal err="${err instanceof Error ? err.message : String(err)}"`);
|
|
269
274
|
releaseLock();
|
|
270
275
|
process.exit(1);
|
|
271
276
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-availability.js","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"publish-availability.js","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"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"}
|
|
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
|
|
|
180
180
|
`code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
|
|
181
181
|
});
|
|
182
182
|
emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
|
|
183
|
+
// Task 2356 — present argv as a direct `node <entry>` invocation before the
|
|
184
|
+
// entry loads. The shim is meant to be transparent: an entry that asks the
|
|
185
|
+
// standard "am I the entry module?" question
|
|
186
|
+
// (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
|
|
187
|
+
// must get the same answer it gets when Claude Code runs it without the shim.
|
|
188
|
+
// With the shim's own path left in argv[1] the answer is no, so such an entry
|
|
189
|
+
// loads, starts nothing, and exits 0 — measured on the three channel servers,
|
|
190
|
+
// which all gate main() that way. Plugin MCPs connect at module top level and
|
|
191
|
+
// never read argv, so this is inert for them. ENTRY was captured above, before
|
|
192
|
+
// the rewrite. Extra CLI args keep their positions after the entry slot.
|
|
193
|
+
process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
|
|
183
194
|
// Run the real MCP server in THIS process. Dynamic import() (not top-level
|
|
184
195
|
// await — this file compiles to CommonJS) loads the ESM entry; a load-time
|
|
185
196
|
// failure mirrors the old child spawn-error path (op=error, exit 127). The
|
|
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
|
|
|
180
180
|
`code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
|
|
181
181
|
});
|
|
182
182
|
emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
|
|
183
|
+
// Task 2356 — present argv as a direct `node <entry>` invocation before the
|
|
184
|
+
// entry loads. The shim is meant to be transparent: an entry that asks the
|
|
185
|
+
// standard "am I the entry module?" question
|
|
186
|
+
// (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
|
|
187
|
+
// must get the same answer it gets when Claude Code runs it without the shim.
|
|
188
|
+
// With the shim's own path left in argv[1] the answer is no, so such an entry
|
|
189
|
+
// loads, starts nothing, and exits 0 — measured on the three channel servers,
|
|
190
|
+
// which all gate main() that way. Plugin MCPs connect at module top level and
|
|
191
|
+
// never read argv, so this is inert for them. ENTRY was captured above, before
|
|
192
|
+
// the rewrite. Extra CLI args keep their positions after the entry slot.
|
|
193
|
+
process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
|
|
183
194
|
// Run the real MCP server in THIS process. Dynamic import() (not top-level
|
|
184
195
|
// await — this file compiles to CommonJS) loads the ESM entry; a load-time
|
|
185
196
|
// failure mirrors the old child spawn-error path (op=error, exit 127). The
|
|
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
|
|
|
180
180
|
`code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
|
|
181
181
|
});
|
|
182
182
|
emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
|
|
183
|
+
// Task 2356 — present argv as a direct `node <entry>` invocation before the
|
|
184
|
+
// entry loads. The shim is meant to be transparent: an entry that asks the
|
|
185
|
+
// standard "am I the entry module?" question
|
|
186
|
+
// (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
|
|
187
|
+
// must get the same answer it gets when Claude Code runs it without the shim.
|
|
188
|
+
// With the shim's own path left in argv[1] the answer is no, so such an entry
|
|
189
|
+
// loads, starts nothing, and exits 0 — measured on the three channel servers,
|
|
190
|
+
// which all gate main() that way. Plugin MCPs connect at module top level and
|
|
191
|
+
// never read argv, so this is inert for them. ENTRY was captured above, before
|
|
192
|
+
// the rewrite. Extra CLI args keep their positions after the entry slot.
|
|
193
|
+
process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
|
|
183
194
|
// Run the real MCP server in THIS process. Dynamic import() (not top-level
|
|
184
195
|
// await — this file compiles to CommonJS) loads the ESM entry; a load-time
|
|
185
196
|
// failure mirrors the old child spawn-error path (op=error, exit 127). The
|