@rubytech/create-sitedesk-code 0.1.429 → 0.1.432

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 (144) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +69 -3
  3. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  4. package/payload/platform/plugins/docs/references/internals.md +2 -2
  5. package/payload/platform/plugins/docs/references/workflows.md +61 -0
  6. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +8 -1
  7. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -1
  8. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +8 -1
  9. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -1
  10. package/payload/platform/plugins/workflows/PLUGIN.md +29 -9
  11. package/payload/platform/plugins/workflows/mcp/dist/index.js +34 -0
  12. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  13. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts +2 -2
  14. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts.map +1 -1
  15. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js.map +1 -1
  16. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts +25 -0
  17. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts.map +1 -1
  18. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +14 -1
  19. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -1
  20. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts +25 -1
  21. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -1
  22. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +79 -15
  23. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  24. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts +2 -0
  25. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts.map +1 -1
  26. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js +4 -0
  27. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js.map +1 -1
  28. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts +3 -1
  29. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -1
  30. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +8 -1
  31. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -1
  32. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +24 -3
  33. package/payload/platform/services/telegram-channel/dist/instructions.js +1 -1
  34. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  35. package/payload/platform/services/telegram-channel/dist/notification.d.ts +10 -5
  36. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  37. package/payload/platform/services/telegram-channel/dist/notification.js +9 -6
  38. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  39. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
  40. package/payload/platform/services/telegram-channel/dist/server.js +15 -5
  41. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  42. package/payload/platform/services/webchat-channel/dist/instructions.js +1 -1
  43. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  44. package/payload/platform/services/webchat-channel/dist/notification.d.ts +12 -10
  45. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  46. package/payload/platform/services/webchat-channel/dist/notification.js +12 -11
  47. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  48. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  49. package/payload/platform/services/webchat-channel/dist/server.js +12 -1
  50. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  51. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +11 -6
  52. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  53. package/payload/platform/services/whatsapp-channel/dist/notification.js +11 -8
  54. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  55. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  56. package/payload/platform/services/whatsapp-channel/dist/server.js +57 -14
  57. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  58. package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +26 -2
  59. package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
  60. package/payload/platform/services/whatsapp-channel/dist/targets.js +50 -2
  61. package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
  62. package/payload/premium-plugins/sitedesk/lib/mcp-lifeline/dist/index.d.ts +8 -0
  63. package/payload/premium-plugins/sitedesk/lib/mcp-lifeline/dist/index.d.ts.map +1 -0
  64. package/payload/premium-plugins/sitedesk/lib/mcp-lifeline/dist/index.js +71 -0
  65. package/payload/premium-plugins/sitedesk/lib/mcp-lifeline/dist/index.js.map +1 -0
  66. package/payload/premium-plugins/sitedesk/lib/mcp-lifeline/package.json +7 -0
  67. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/PLUGIN.md +39 -3
  68. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/index.d.ts +2 -0
  69. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/index.d.ts.map +1 -0
  70. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/index.js +32 -0
  71. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/index.js.map +1 -0
  72. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/account.d.ts +10 -0
  73. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/account.d.ts.map +1 -0
  74. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/account.js +21 -0
  75. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/account.js.map +1 -0
  76. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/engine-run.d.ts +24 -0
  77. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/engine-run.d.ts.map +1 -0
  78. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/engine-run.js +59 -0
  79. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/engine-run.js.map +1 -0
  80. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.d.ts +22 -0
  81. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.d.ts.map +1 -0
  82. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.js +40 -0
  83. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.js.map +1 -0
  84. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/geometry.d.ts +47 -0
  85. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/geometry.d.ts.map +1 -0
  86. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/geometry.js +60 -0
  87. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/geometry.js.map +1 -0
  88. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/log.d.ts +7 -0
  89. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/log.d.ts.map +1 -0
  90. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/log.js +9 -0
  91. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/log.js.map +1 -0
  92. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.d.ts +29 -0
  93. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.d.ts.map +1 -0
  94. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.js +113 -0
  95. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.js.map +1 -0
  96. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-engine-run.d.ts +3 -0
  97. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-engine-run.d.ts.map +1 -0
  98. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-engine-run.js +48 -0
  99. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-engine-run.js.map +1 -0
  100. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-render.d.ts +3 -0
  101. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-render.d.ts.map +1 -0
  102. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-render.js +50 -0
  103. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/quote-render.js.map +1 -0
  104. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/takeoff-compute.d.ts +3 -0
  105. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/takeoff-compute.d.ts.map +1 -0
  106. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/takeoff-compute.js +42 -0
  107. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/tools/takeoff-compute.js.map +1 -0
  108. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/package-lock.json +2684 -0
  109. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/package.json +21 -0
  110. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/index.ts +39 -0
  111. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/account.test.ts +49 -0
  112. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/engine-run.test.ts +105 -0
  113. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/gates.test.ts +51 -0
  114. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/geometry.test.ts +83 -0
  115. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/render-run.test.ts +111 -0
  116. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/account.ts +20 -0
  117. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/engine-run.ts +80 -0
  118. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/gates.ts +39 -0
  119. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/geometry.ts +93 -0
  120. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/log.ts +8 -0
  121. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/render-run.ts +125 -0
  122. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/tools/quote-engine-run.ts +66 -0
  123. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/tools/quote-render.ts +66 -0
  124. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/tools/takeoff-compute.ts +51 -0
  125. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/tsconfig.json +20 -0
  126. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/skills/quotation/references/quote-engine.md +24 -0
  127. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/skills/quotation/references/quote-generation.md +67 -51
  128. package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/skills/takeoff/SKILL.md +12 -0
  129. package/payload/server/public/assets/{AdminLoginScreens-C4AZT7cQ.js → AdminLoginScreens-BM45lL5G.js} +1 -1
  130. package/payload/server/public/assets/{admin-SaF1F4e-.js → admin-B8Eb9Wbj.js} +1 -1
  131. package/payload/server/public/assets/{audio-attachment-mime-BBW07eyG.js → audio-attachment-mime-Cx5d3Jls.js} +1 -1
  132. package/payload/server/public/assets/{chat-B9Qjt7C9.js → chat-LEEUAJOG.js} +1 -1
  133. package/payload/server/public/assets/data-CKojMQSz.js +1 -0
  134. package/payload/server/public/assets/{operator-CTJjI8te.js → operator-OwY_sWTH.js} +1 -1
  135. package/payload/server/public/assets/{page-CbI1wtef.js → page-B7nCws9K.js} +1 -1
  136. package/payload/server/public/assets/{page-JzxyFGle.js → page-C3L6-Ufk.js} +5 -5
  137. package/payload/server/public/assets/{public-DzeW3_eM.js → public-CSLXSSy5.js} +1 -1
  138. package/payload/server/public/chat.html +3 -3
  139. package/payload/server/public/data.html +3 -3
  140. package/payload/server/public/index.html +4 -4
  141. package/payload/server/public/operator.html +4 -4
  142. package/payload/server/public/public.html +3 -3
  143. package/payload/server/server.js +1647 -1470
  144. package/payload/server/public/assets/data-BKE6Sa-g.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,8EAA8E;AAC9E,uFAAuF;AACvF,sFAAsF;AACtF,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,8EAA8E;AAC9E,qEAAqE;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;AAEpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAE9E,OAAO,CAAC,0CAA0C,SAAS,EAAE,CAAC,CAAC;AAE/D,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAE5B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAEhC,SAAS,QAAQ;IACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resolve the account-scoped workspace root. `ACCOUNT_DIR` is the canonical
3
+ * spawn-set var; fall back to `PLATFORM_ROOT` + `ACCOUNT_ID` exactly as the
4
+ * memory plugin's kd-classify resolver does. Returns `null` when neither is
5
+ * resolvable — the caller raises a precise no-account-context fault rather than
6
+ * running an engine at an empty path. Reading env per call (never at module
7
+ * init) keeps plugin enumeration with empty env from faulting.
8
+ */
9
+ export declare function resolveAccountDir(): string | null;
10
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/lib/account.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CASjD"}
@@ -0,0 +1,21 @@
1
+ import { join } from "node:path";
2
+ /**
3
+ * Resolve the account-scoped workspace root. `ACCOUNT_DIR` is the canonical
4
+ * spawn-set var; fall back to `PLATFORM_ROOT` + `ACCOUNT_ID` exactly as the
5
+ * memory plugin's kd-classify resolver does. Returns `null` when neither is
6
+ * resolvable — the caller raises a precise no-account-context fault rather than
7
+ * running an engine at an empty path. Reading env per call (never at module
8
+ * init) keeps plugin enumeration with empty env from faulting.
9
+ */
10
+ export function resolveAccountDir() {
11
+ const direct = process.env.ACCOUNT_DIR;
12
+ if (direct && direct.trim())
13
+ return direct;
14
+ const platformRoot = process.env.PLATFORM_ROOT;
15
+ const accountId = process.env.ACCOUNT_ID;
16
+ if (platformRoot && accountId && !accountId.includes("/") && !accountId.includes("..")) {
17
+ return join(platformRoot, "..", "data", "accounts", accountId);
18
+ }
19
+ return null;
20
+ }
21
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/lib/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACvC,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACzC,IAAI,YAAY,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvF,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,24 @@
1
+ export type EngineFault = "engine-absent" | "method-absent" | "script-error";
2
+ export type EngineMode = "quote" | "verify";
3
+ export type EngineResult = {
4
+ ok: true;
5
+ figures: Record<string, unknown>;
6
+ } | {
7
+ ok: false;
8
+ reason: EngineFault;
9
+ detail: string;
10
+ };
11
+ export interface RunEngineArgs {
12
+ accountDir: string;
13
+ enginePath: string;
14
+ methodPath: string;
15
+ job: unknown;
16
+ mode: EngineMode;
17
+ }
18
+ /** Spawn `node <enginePath>` in the account workspace, feed the job on stdin,
19
+ * and return the parsed figures. Existence of engine and method is gated first
20
+ * so a missing artifact is a named fault, never a script crash. */
21
+ export declare function runEngine(a: RunEngineArgs): Promise<EngineResult>;
22
+ /** Persist figures to quoting/jobs/<jobId>/figures.json and return the path. */
23
+ export declare function writeFigures(accountDir: string, jobId: string, figures: unknown): string;
24
+ //# sourceMappingURL=engine-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-run.d.ts","sourceRoot":"","sources":["../../src/lib/engine-run.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,eAAe,GAAG,cAAc,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC9C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;oEAEoE;AACpE,wBAAsB,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA2CvE;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAKxF"}
@@ -0,0 +1,59 @@
1
+ // Execute the operator's already-persisted pricing engine as a child process.
2
+ // The engine is the operator's own bespoke script; this module never regenerates
3
+ // it, never parses the method, and embeds no rate/catalogue/roll-up. It fixes the
4
+ // I/O boundary a generated engine conforms to: {methodPath, job, mode} on stdin,
5
+ // figures JSON on stdout. A fault returns zero figures and a distinct cause.
6
+ import { spawn } from "node:child_process";
7
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ /** Spawn `node <enginePath>` in the account workspace, feed the job on stdin,
10
+ * and return the parsed figures. Existence of engine and method is gated first
11
+ * so a missing artifact is a named fault, never a script crash. */
12
+ export async function runEngine(a) {
13
+ if (!existsSync(a.enginePath)) {
14
+ return { ok: false, reason: "engine-absent", detail: `no engine at ${a.enginePath}` };
15
+ }
16
+ if (!existsSync(a.methodPath)) {
17
+ return { ok: false, reason: "method-absent", detail: `no method at ${a.methodPath}` };
18
+ }
19
+ const payload = JSON.stringify({ methodPath: a.methodPath, job: a.job, mode: a.mode });
20
+ const { code, stdout, stderr } = await new Promise((resolve) => {
21
+ const child = spawn("node", [a.enginePath], { cwd: a.accountDir });
22
+ let out = "";
23
+ let err = "";
24
+ child.stdout.on("data", (d) => (out += d));
25
+ child.stderr.on("data", (d) => (err += d));
26
+ child.on("error", (e) => resolve({ code: -1, stdout: out, stderr: String(e) }));
27
+ child.on("close", (c) => resolve({ code: c, stdout: out, stderr: err }));
28
+ // A script that exits before draining stdin closes the read end mid-write,
29
+ // raising EPIPE. Swallow it: the exit code from `close` carries the fault, so
30
+ // an early exit surfaces as script-error rather than crashing the server.
31
+ child.stdin.on("error", () => { });
32
+ child.stdin.write(payload);
33
+ child.stdin.end();
34
+ });
35
+ if (code !== 0) {
36
+ return { ok: false, reason: "script-error", detail: `engine exited ${code}: ${stderr.trim().slice(0, 400)}` };
37
+ }
38
+ let figures;
39
+ try {
40
+ figures = JSON.parse(stdout);
41
+ }
42
+ catch {
43
+ return { ok: false, reason: "script-error", detail: `engine stdout was not JSON: ${stdout.trim().slice(0, 200)}` };
44
+ }
45
+ const required = a.mode === "quote" ? ["total", "reconcile"] : ["deltas"];
46
+ const missing = required.filter((k) => !(k in figures));
47
+ if (missing.length > 0) {
48
+ return { ok: false, reason: "script-error", detail: `engine output missing required field(s): ${missing.join(", ")}` };
49
+ }
50
+ return { ok: true, figures };
51
+ }
52
+ /** Persist figures to quoting/jobs/<jobId>/figures.json and return the path. */
53
+ export function writeFigures(accountDir, jobId, figures) {
54
+ const path = join(accountDir, "quoting", "jobs", jobId, "figures.json");
55
+ mkdirSync(dirname(path), { recursive: true });
56
+ writeFileSync(path, JSON.stringify(figures, null, 2));
57
+ return path;
58
+ }
59
+ //# sourceMappingURL=engine-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-run.js","sourceRoot":"","sources":["../../src/lib/engine-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface ReconcileVerdict {
2
+ pass: boolean;
3
+ shown?: number;
4
+ }
5
+ export interface LeakVerdict {
6
+ pass: boolean;
7
+ }
8
+ export interface SignLinkVerdict {
9
+ pass: boolean;
10
+ href?: string;
11
+ reason?: "no-anchor" | "empty" | "fragment" | "non-https" | "relative";
12
+ }
13
+ /** The document's displayed grand total (data-quote-total) must equal the
14
+ * engine total. No marker → fail closed (an unverifiable total is not shipped). */
15
+ export declare function gateReconcile(html: string, engineTotal: number): ReconcileVerdict;
16
+ /** A client document carrying a cost/margin marker (data-internal-only) is a
17
+ * leak — the boundary is structural, not a matter of remembering to omit. */
18
+ export declare function gateMarginLeak(html: string): LeakVerdict;
19
+ /** The client quote's Sign-Online anchor href must be an absolute https URL —
20
+ * the same verdict ladder the quote-generation link-liveness gate applies. */
21
+ export declare function gateSignLink(html: string): SignLinkVerdict;
22
+ //# sourceMappingURL=gates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AACnE,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,OAAO,CAAA;CAAE;AAC9C,MAAM,WAAW,eAAe;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAA;CAAE;AAEzI;oFACoF;AACpF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAMjF;AAED;8EAC8E;AAC9E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAExD;AAED;+EAC+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAS1D"}
@@ -0,0 +1,40 @@
1
+ // The three deterministic, fail-closed gates quote-render enforces on the HTML
2
+ // the operator's render script emits. They are structural checks over stable
3
+ // attributes the render script marks — not business content:
4
+ // data-quote-total the displayed grand total (reconcile against the engine)
5
+ // data-internal-only cost/margin-bearing figures (must be absent from client docs)
6
+ // data-esign-accept the client quote's Sign-Online anchor (absolute https only)
7
+ /** The document's displayed grand total (data-quote-total) must equal the
8
+ * engine total. No marker → fail closed (an unverifiable total is not shipped). */
9
+ export function gateReconcile(html, engineTotal) {
10
+ const m = html.match(/data-quote-total="([^"]*)"/i);
11
+ if (!m || m[1].trim() === "")
12
+ return { pass: false };
13
+ const shown = Number(m[1]);
14
+ if (!Number.isFinite(shown))
15
+ return { pass: false };
16
+ return { pass: shown === engineTotal, shown };
17
+ }
18
+ /** A client document carrying a cost/margin marker (data-internal-only) is a
19
+ * leak — the boundary is structural, not a matter of remembering to omit. */
20
+ export function gateMarginLeak(html) {
21
+ return { pass: !/\bdata-internal-only\b/i.test(html) };
22
+ }
23
+ /** The client quote's Sign-Online anchor href must be an absolute https URL —
24
+ * the same verdict ladder the quote-generation link-liveness gate applies. */
25
+ export function gateSignLink(html) {
26
+ const tag = html.match(/<a[^>]*\bdata-esign-accept\b[^>]*>/i)?.[0];
27
+ if (!tag)
28
+ return { pass: false, reason: "no-anchor" };
29
+ const href = tag.match(/href="([^"]*)"/i)?.[1] ?? "";
30
+ if (/^https:\/\//i.test(href))
31
+ return { pass: true, href };
32
+ if (href === "")
33
+ return { pass: false, href, reason: "empty" };
34
+ if (href.startsWith("#"))
35
+ return { pass: false, href, reason: "fragment" };
36
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href))
37
+ return { pass: false, href, reason: "non-https" };
38
+ return { pass: false, href, reason: "relative" };
39
+ }
40
+ //# sourceMappingURL=gates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,6EAA6E;AAC7E,6DAA6D;AAC7D,gFAAgF;AAChF,qFAAqF;AACrF,mFAAmF;AAMnF;oFACoF;AACpF,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,WAAmB;IAC7D,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACpD,OAAO,EAAE,IAAI,EAAE,KAAK,KAAK,WAAW,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC;AAED;8EAC8E;AAC9E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,EAAE,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzD,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,qCAAqC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnE,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACtD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3D,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC/D,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC3E,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACzF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AACnD,CAAC"}
@@ -0,0 +1,47 @@
1
+ export type Basis = "printed" | "scaled" | "count" | "operator";
2
+ /** A drawing scale: a stated ratio (1:R → R), or a known-dimension calibration
3
+ * where `actual/measuredRef` is the factor. */
4
+ export type Scale = {
5
+ ratio: number;
6
+ } | {
7
+ measuredRef: number;
8
+ actual: number;
9
+ };
10
+ export interface InLine {
11
+ element: string;
12
+ basis: Basis;
13
+ value?: number;
14
+ unit?: string;
15
+ measured?: number;
16
+ measuredB?: number;
17
+ kind?: "length" | "area";
18
+ count?: number | number[];
19
+ crossCheck?: {
20
+ value: number;
21
+ unit?: string;
22
+ tolerance?: number;
23
+ };
24
+ }
25
+ export interface OutLine {
26
+ element: string;
27
+ quantity: number;
28
+ unit?: string;
29
+ basis: Basis;
30
+ crossCheck?: {
31
+ printed: number;
32
+ delta: number;
33
+ disagree: boolean;
34
+ };
35
+ }
36
+ export interface TakeoffResult {
37
+ lines: OutLine[];
38
+ counts: {
39
+ exact: number;
40
+ scaled: number;
41
+ };
42
+ }
43
+ export declare function computeTakeoff(input: {
44
+ scale?: Scale;
45
+ lines: InLine[];
46
+ }): TakeoffResult;
47
+ //# sourceMappingURL=geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/lib/geometry.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAEhE;gDACgD;AAChD,MAAM,MAAM,KAAK,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CACpE;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AA0CD,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,aAAa,CAevF"}
@@ -0,0 +1,60 @@
1
+ // Deterministic takeoff geometry. Pure arithmetic over agent-supplied measured
2
+ // inputs — scale to a real dimension, a scaled area from two sides, and a count
3
+ // roll-up — plus a cross-check of a scaled quantity against a printed figure.
4
+ // Reading the drawing and eliciting the inputs is the takeoff skill's job, not
5
+ // this module's. No business content: it multiplies numbers, it prices nothing.
6
+ function scaleFactor(scale, element) {
7
+ if (!scale)
8
+ throw new Error(`takeoff: line "${element}" is scale-derived but no scale was supplied`);
9
+ const factor = "ratio" in scale ? scale.ratio : scale.actual / scale.measuredRef;
10
+ if (!Number.isFinite(factor)) {
11
+ throw new Error(`takeoff: line "${element}" has a degenerate scale (factor=${factor})`);
12
+ }
13
+ return factor;
14
+ }
15
+ function quantityFor(line, scale) {
16
+ switch (line.basis) {
17
+ case "printed":
18
+ case "operator": {
19
+ if (typeof line.value !== "number") {
20
+ throw new Error(`takeoff: line "${line.element}" (${line.basis}) is missing its value`);
21
+ }
22
+ return line.value;
23
+ }
24
+ case "count": {
25
+ if (line.count === undefined) {
26
+ throw new Error(`takeoff: line "${line.element}" (count) is missing its count`);
27
+ }
28
+ return Array.isArray(line.count) ? line.count.reduce((a, b) => a + b, 0) : line.count;
29
+ }
30
+ case "scaled": {
31
+ if (typeof line.measured !== "number") {
32
+ throw new Error(`takeoff: line "${line.element}" (scaled) is missing its measured length`);
33
+ }
34
+ const factor = scaleFactor(scale, line.element);
35
+ if (line.kind === "area") {
36
+ if (typeof line.measuredB !== "number") {
37
+ throw new Error(`takeoff: line "${line.element}" (scaled area) is missing its second side`);
38
+ }
39
+ return line.measured * line.measuredB * factor * factor;
40
+ }
41
+ return line.measured * factor;
42
+ }
43
+ }
44
+ }
45
+ export function computeTakeoff(input) {
46
+ const lines = input.lines.map((line) => {
47
+ const quantity = quantityFor(line, input.scale);
48
+ const out = { element: line.element, quantity, unit: line.unit, basis: line.basis };
49
+ if (line.basis === "scaled" && line.crossCheck) {
50
+ const printed = line.crossCheck.value;
51
+ const delta = quantity - printed;
52
+ const tolerance = line.crossCheck.tolerance ?? 0;
53
+ out.crossCheck = { printed, delta, disagree: Math.abs(delta) > tolerance };
54
+ }
55
+ return out;
56
+ });
57
+ const scaled = lines.filter((l) => l.basis === "scaled").length;
58
+ return { lines, counts: { exact: lines.length - scaled, scaled } };
59
+ }
60
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/lib/geometry.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAiChF,SAAS,WAAW,CAAC,KAAwB,EAAE,OAAe;IAC5D,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,8CAA8C,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC;IACjF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,oCAAoC,MAAM,GAAG,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAwB;IACzD,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,KAAK,SAAS,CAAC;QACf,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,OAAO,MAAM,IAAI,CAAC,KAAK,wBAAwB,CAAC,CAAC;YAC1F,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,OAAO,gCAAgC,CAAC,CAAC;YAClF,CAAC;YACD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QACxF,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,OAAO,2CAA2C,CAAC,CAAC;YAC7F,CAAC;YACD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAChD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzB,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;oBACvC,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,OAAO,4CAA4C,CAAC,CAAC;gBAC9F,CAAC;gBACD,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;YAC1D,CAAC;YACD,OAAO,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;QAChC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAyC;IACtE,MAAM,KAAK,GAAc,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAChD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,GAAG,GAAY,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7F,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YACtC,MAAM,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,IAAI,CAAC,CAAC;YACjD,GAAG,CAAC,UAAU,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IAChE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;AACrE,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Emit one structured observability line to stderr. Every tool invocation logs
3
+ * one line here, correlated by `jobId`; journald captures stderr, so the
4
+ * diagnostic path is `journalctl --user | grep -E '\\[quote-engine\\]|...'`.
5
+ */
6
+ export declare function logLine(s: string): void;
7
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAEvC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Emit one structured observability line to stderr. Every tool invocation logs
3
+ * one line here, correlated by `jobId`; journald captures stderr, so the
4
+ * diagnostic path is `journalctl --user | grep -E '\\[quote-engine\\]|...'`.
5
+ */
6
+ export function logLine(s) {
7
+ process.stderr.write(s + "\n");
8
+ }
9
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type SignLinkVerdict } from "./gates.js";
2
+ export type RenderFault = "render-absent" | "render-error" | "reconcile-fail" | "margin-leak" | "sign-link";
3
+ export interface RunRenderArgs {
4
+ accountDir: string;
5
+ renderScriptPath: string;
6
+ figures: Record<string, unknown>;
7
+ signUrl?: string;
8
+ jobId: string;
9
+ }
10
+ export type RenderResult = {
11
+ ok: true;
12
+ docs: {
13
+ internal: string;
14
+ breakdown: string;
15
+ quote: string;
16
+ };
17
+ reconcile: {
18
+ internal: boolean;
19
+ breakdown: boolean;
20
+ quote: boolean;
21
+ };
22
+ signLink: SignLinkVerdict;
23
+ } | {
24
+ ok: false;
25
+ reason: RenderFault;
26
+ detail: string;
27
+ };
28
+ export declare function runRender(a: RunRenderArgs): Promise<RenderResult>;
29
+ //# sourceMappingURL=render-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-run.d.ts","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"AAYA,OAAO,EAA+C,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,GAAG,WAAW,CAAC;AAE5G,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GACpB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,SAAS,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IACrE,QAAQ,EAAE,eAAe,CAAC;CAC3B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAIvD,wBAAsB,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAyFvE"}
@@ -0,0 +1,113 @@
1
+ // Execute the operator's persisted render script (quoting/render.mjs) to turn
2
+ // figures into the three reconciling HTML documents, then enforce the three
3
+ // deterministic gates and file each doc by its scope. The render script owns
4
+ // brand/layout as its own data; this module templates nothing. HTML only — the
5
+ // PDF is produced downstream by the browser plugin's browser-pdf-save.
6
+ //
7
+ // Fail-closed and atomic: if any gate fails, no document is filed and a
8
+ // cause-naming fault is returned. "Fail loudly and stop", never a partial issue.
9
+ import { spawn } from "node:child_process";
10
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ import { gateReconcile, gateMarginLeak, gateSignLink } from "./gates.js";
13
+ export async function runRender(a) {
14
+ if (!existsSync(a.renderScriptPath)) {
15
+ return { ok: false, reason: "render-absent", detail: `no render script at ${a.renderScriptPath}` };
16
+ }
17
+ const payload = JSON.stringify({ figures: a.figures, signUrl: a.signUrl ?? null });
18
+ const { code, stdout, stderr } = await new Promise((resolve) => {
19
+ const child = spawn("node", [a.renderScriptPath], { cwd: a.accountDir });
20
+ let out = "";
21
+ let err = "";
22
+ child.stdout.on("data", (d) => (out += d));
23
+ child.stderr.on("data", (d) => (err += d));
24
+ child.on("error", (e) => resolve({ code: -1, stdout: out, stderr: String(e) }));
25
+ child.on("close", (c) => resolve({ code: c, stdout: out, stderr: err }));
26
+ // A script that exits before draining stdin closes the read end mid-write,
27
+ // raising EPIPE. Swallow it: the exit code from `close` carries the fault, so
28
+ // an early exit surfaces as render-error rather than crashing the server.
29
+ child.stdin.on("error", () => { });
30
+ child.stdin.write(payload);
31
+ child.stdin.end();
32
+ });
33
+ if (code !== 0) {
34
+ return { ok: false, reason: "render-error", detail: `render script exited ${code}: ${stderr.trim().slice(0, 400)}` };
35
+ }
36
+ let html;
37
+ try {
38
+ const parsed = JSON.parse(stdout);
39
+ if (typeof parsed.internal !== "string" || typeof parsed.breakdown !== "string" || typeof parsed.quote !== "string") {
40
+ return { ok: false, reason: "render-error", detail: "render output must be { internal, breakdown, quote } HTML strings" };
41
+ }
42
+ html = parsed;
43
+ }
44
+ catch {
45
+ return { ok: false, reason: "render-error", detail: `render stdout was not JSON: ${stdout.trim().slice(0, 200)}` };
46
+ }
47
+ const engineTotal = Number(a.figures.total);
48
+ const phone = a.figures.header?.phone;
49
+ if (!phone) {
50
+ return { ok: false, reason: "render-error", detail: "figures.header.phone is required to file the documents by scope" };
51
+ }
52
+ // The phone is a filing-path segment; reject a separator or traversal so a
53
+ // crafted header cannot escape the customer scope, the same guard account.ts
54
+ // applies to ACCOUNT_ID.
55
+ if (phone.includes("/") || phone.includes("..")) {
56
+ return { ok: false, reason: "render-error", detail: `figures.header.phone contains a path separator or traversal: ${phone}` };
57
+ }
58
+ // Gate every document before filing any, so the log reports each doc's verdict
59
+ // and nothing is written when one fails.
60
+ const recInternal = gateReconcile(html.internal, engineTotal);
61
+ const recBreakdown = gateReconcile(html.breakdown, engineTotal);
62
+ const recQuote = gateReconcile(html.quote, engineTotal);
63
+ const leakBreakdown = gateMarginLeak(html.breakdown);
64
+ const leakQuote = gateMarginLeak(html.quote);
65
+ const signLink = gateSignLink(html.quote);
66
+ const internalOk = recInternal.pass;
67
+ const breakdownOk = recBreakdown.pass && leakBreakdown.pass;
68
+ const quoteOk = recQuote.pass && leakQuote.pass && signLink.pass;
69
+ if (!internalOk || !breakdownOk || !quoteOk) {
70
+ // Reason names the first failing gate, in document then gate order.
71
+ let reason;
72
+ let detail;
73
+ if (!recInternal.pass) {
74
+ reason = "reconcile-fail";
75
+ detail = `internal total ${recInternal.shown} != engine ${engineTotal}`;
76
+ }
77
+ else if (!recBreakdown.pass) {
78
+ reason = "reconcile-fail";
79
+ detail = `breakdown total ${recBreakdown.shown} != engine ${engineTotal}`;
80
+ }
81
+ else if (!leakBreakdown.pass) {
82
+ reason = "margin-leak";
83
+ detail = "breakdown carries data-internal-only";
84
+ }
85
+ else if (!recQuote.pass) {
86
+ reason = "reconcile-fail";
87
+ detail = `quote total ${recQuote.shown} != engine ${engineTotal}`;
88
+ }
89
+ else if (!leakQuote.pass) {
90
+ reason = "margin-leak";
91
+ detail = "quote carries data-internal-only";
92
+ }
93
+ else {
94
+ reason = "sign-link";
95
+ detail = `quote Sign-Online link ${signLink.reason} (href=${signLink.href ?? ""})`;
96
+ }
97
+ return { ok: false, reason, detail };
98
+ }
99
+ const internalPath = join(a.accountDir, "memory/admin/customers", phone, `quote-${a.jobId}-internal.html`);
100
+ const breakdownPath = join(a.accountDir, "memory/users", phone, "documents", `quote-${a.jobId}-breakdown.html`);
101
+ const quotePath = join(a.accountDir, "memory/users", phone, "documents", `quote-${a.jobId}.html`);
102
+ for (const [p, body] of [[internalPath, html.internal], [breakdownPath, html.breakdown], [quotePath, html.quote]]) {
103
+ mkdirSync(dirname(p), { recursive: true });
104
+ writeFileSync(p, body);
105
+ }
106
+ return {
107
+ ok: true,
108
+ docs: { internal: internalPath, breakdown: breakdownPath, quote: quotePath },
109
+ reconcile: { internal: internalOk, breakdown: breakdownOk, quote: quoteOk },
110
+ signLink,
111
+ };
112
+ }
113
+ //# sourceMappingURL=render-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-run.js","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerQuoteEngineRun(server: McpServer): void;
3
+ //# sourceMappingURL=quote-engine-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-engine-run.d.ts","sourceRoot":"","sources":["../../src/tools/quote-engine-run.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoD9D"}
@@ -0,0 +1,48 @@
1
+ // quote-engine-run — execute the operator's persisted pricing engine against a
2
+ // job and return structured figures. Runs the already-persisted engine; never
3
+ // regenerates it, never embeds a rate/catalogue/roll-up. A fault returns a
4
+ // cause-naming error and zero figures — never a partial or guessed total.
5
+ import { z } from "zod";
6
+ import { join } from "node:path";
7
+ import { lifelineTool } from "../../../../../lib/mcp-lifeline/dist/index.js";
8
+ import { resolveAccountDir } from "../lib/account.js";
9
+ import { runEngine, writeFigures } from "../lib/engine-run.js";
10
+ import { logLine } from "../lib/log.js";
11
+ export function registerQuoteEngineRun(server) {
12
+ lifelineTool(server, "quote-engine-run", "Execute the operator's already-persisted pricing engine (quoting/engine.mjs) against a job's quantities and return line prices, group subtotals and total. mode=verify runs the engine against a past job and returns the per-level reproduction delta. Runs the operator's own script; it is not a pricing model and embeds no rate, catalogue or roll-up. Faults (engine-absent, method-absent, script-error) return zero figures.", {
13
+ jobId: z.string().describe("Correlation id; figures persist under quoting/jobs/<jobId>/."),
14
+ job: z.record(z.any()).describe("The job: header + lines. Passed to the engine verbatim; not interpreted here."),
15
+ mode: z.enum(["quote", "verify"]).default("quote").describe("quote = price the job; verify = reproduce a past job and return the delta."),
16
+ enginePath: z.string().default("quoting/engine.mjs").describe("Engine path relative to the account workspace."),
17
+ methodPath: z.string().default("quoting/method.json").describe("Method path relative to the account workspace."),
18
+ }, async (params) => {
19
+ const { jobId, job, mode, enginePath, methodPath } = params;
20
+ const accountId = process.env.ACCOUNT_ID ?? "local";
21
+ const accountDir = resolveAccountDir();
22
+ if (!accountDir) {
23
+ const detail = "ACCOUNT_DIR unset and PLATFORM_ROOT+ACCOUNT_ID unavailable";
24
+ logLine(`[quote-engine] op=fault jobId=${jobId} reason=no-account-context detail=${detail}`);
25
+ return { content: [{ type: "text", text: JSON.stringify({ ok: false, reason: "no-account-context", detail }) }], isError: true };
26
+ }
27
+ const absEngine = join(accountDir, enginePath);
28
+ const absMethod = join(accountDir, methodPath);
29
+ const r = await runEngine({ accountDir, enginePath: absEngine, methodPath: absMethod, job, mode });
30
+ if (!r.ok) {
31
+ logLine(`[quote-engine] op=fault jobId=${jobId} reason=${r.reason} detail=${r.detail}`);
32
+ return { content: [{ type: "text", text: JSON.stringify({ ok: false, reason: r.reason, detail: r.detail }, null, 2) }], isError: true };
33
+ }
34
+ if (mode === "verify") {
35
+ const deltas = r.figures.deltas ?? [];
36
+ const worst = r.figures.worst ?? 0;
37
+ logLine(`[quote-engine] op=verify jobId=${jobId} accountId=${accountId} worst=${worst} levels=${deltas.length}`);
38
+ return { content: [{ type: "text", text: JSON.stringify({ jobId, deltas, worst }, null, 2) }] };
39
+ }
40
+ const total = r.figures.total;
41
+ const lineCount = Array.isArray(r.figures.lines) ? r.figures.lines.length : 0;
42
+ const reconcile = r.figures.reconcile;
43
+ const figuresPath = writeFigures(accountDir, jobId, r.figures);
44
+ logLine(`[quote-engine] op=run jobId=${jobId} accountId=${accountId} enginePath=${enginePath} methodPath=${methodPath} lines=${lineCount} total=${total}`);
45
+ return { content: [{ type: "text", text: JSON.stringify({ jobId, total, lineCount, reconcile, figuresPath }, null, 2) }] };
46
+ });
47
+ }
48
+ //# sourceMappingURL=quote-engine-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-engine-run.js","sourceRoot":"","sources":["../../src/tools/quote-engine-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerQuoteRender(server: McpServer): void;
3
+ //# sourceMappingURL=quote-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-render.d.ts","sourceRoot":"","sources":["../../src/tools/quote-render.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkD3D"}