@method-ai-hq/sdk 0.0.0-stage → 0.14.2

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -2
  3. package/dist/packages/contracts/src/identity.d.ts +16 -0
  4. package/dist/packages/contracts/src/identity.js +41 -0
  5. package/dist/packages/contracts/src/method-package.d.ts +71 -0
  6. package/dist/packages/contracts/src/method-package.js +22 -0
  7. package/dist/packages/method-document/src/conversion-report.d.ts +138 -0
  8. package/dist/packages/method-document/src/conversion-report.js +23 -0
  9. package/dist/packages/sdk/examples/daily-briefing/README.md +31 -0
  10. package/dist/packages/sdk/examples/daily-briefing/TASK.md +3 -0
  11. package/dist/packages/sdk/examples/daily-briefing/approved-report.md +138 -0
  12. package/dist/packages/sdk/examples/daily-briefing/briefing_artifacts.py +39 -0
  13. package/dist/packages/sdk/examples/daily-briefing/briefing_check_inputs.py +47 -0
  14. package/dist/packages/sdk/examples/daily-briefing/briefing_files.py +31 -0
  15. package/dist/packages/sdk/examples/daily-briefing/briefing_manifest.py +23 -0
  16. package/dist/packages/sdk/examples/daily-briefing/briefing_markdown.py +67 -0
  17. package/dist/packages/sdk/examples/daily-briefing/briefing_progress.py +18 -0
  18. package/dist/packages/sdk/examples/daily-briefing/briefing_render.py +71 -0
  19. package/dist/packages/sdk/examples/daily-briefing/briefing_sessions.py +37 -0
  20. package/dist/packages/sdk/examples/daily-briefing/briefing_times.py +158 -0
  21. package/dist/packages/sdk/examples/daily-briefing/briefing_validation.py +101 -0
  22. package/dist/packages/sdk/examples/daily-briefing/briefing_website.py +215 -0
  23. package/dist/packages/sdk/examples/daily-briefing/checks.json +19 -0
  24. package/dist/packages/sdk/examples/daily-briefing/daily-briefing.method +158 -0
  25. package/dist/packages/sdk/examples/daily-briefing/inputs.json +1 -0
  26. package/dist/packages/sdk/examples/daily-briefing/package-lock.json +10 -0
  27. package/dist/packages/sdk/examples/daily-briefing/package.json +4 -0
  28. package/dist/packages/sdk/examples/daily-briefing/pyproject.toml +8 -0
  29. package/dist/packages/sdk/examples/daily-briefing/reader/reader.css +35 -0
  30. package/dist/packages/sdk/examples/daily-briefing/reader/reader.js +42 -0
  31. package/dist/packages/sdk/examples/daily-briefing/sample/context.json +1 -0
  32. package/dist/packages/sdk/examples/daily-briefing/sample/documents/document-001.md +219 -0
  33. package/dist/packages/sdk/examples/daily-briefing/sample/media.json +1 -0
  34. package/dist/packages/sdk/examples/daily-briefing/sample/metadata/documents.json +39 -0
  35. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0001.json +49 -0
  36. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0002.json +49 -0
  37. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0003.json +49 -0
  38. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0004.json +49 -0
  39. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0005.json +49 -0
  40. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0006.json +49 -0
  41. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0007.json +49 -0
  42. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0008.json +49 -0
  43. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0009.json +49 -0
  44. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0010.json +49 -0
  45. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0011.json +49 -0
  46. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0012.json +49 -0
  47. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0013.json +49 -0
  48. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0014.json +49 -0
  49. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0015.json +49 -0
  50. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0016.json +49 -0
  51. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0017.json +49 -0
  52. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0018.json +49 -0
  53. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0019.json +49 -0
  54. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0020.json +49 -0
  55. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0021.json +49 -0
  56. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0022.json +49 -0
  57. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0023.json +49 -0
  58. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0024.json +49 -0
  59. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0025.json +49 -0
  60. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0026.json +49 -0
  61. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0027.json +49 -0
  62. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0028.json +49 -0
  63. package/dist/packages/sdk/examples/daily-briefing/sample/sessions.json +1 -0
  64. package/dist/packages/sdk/examples/daily-briefing/sample/timeline.jsonl +28 -0
  65. package/dist/packages/sdk/examples/daily-briefing/sample/untimed.jsonl +0 -0
  66. package/dist/packages/sdk/examples/daily-briefing/sample-output.zip +0 -0
  67. package/dist/packages/sdk/examples/daily-briefing/sample-report.md +51 -0
  68. package/dist/packages/sdk/examples/daily-briefing/uv.lock +128 -0
  69. package/dist/packages/sdk/examples/daily-briefing/vendor/marked-LICENSE.md +44 -0
  70. package/dist/packages/sdk/examples/daily-briefing/vendor/marked.mjs +77 -0
  71. package/dist/packages/sdk/examples/files.json +126 -0
  72. package/dist/packages/sdk/examples/message-routing/README.md +23 -0
  73. package/dist/packages/sdk/examples/message-routing/TASK.md +5 -0
  74. package/dist/packages/sdk/examples/message-routing/choose-destination.mjs +10 -0
  75. package/dist/packages/sdk/examples/message-routing/create-ticket.mjs +15 -0
  76. package/dist/packages/sdk/examples/message-routing/inputs.json +1 -0
  77. package/dist/packages/sdk/examples/message-routing/message-routing.method +45 -0
  78. package/dist/packages/sdk/examples/message-routing/result.fixture.json +6 -0
  79. package/dist/packages/sdk/examples/message-routing/routing.mjs +29 -0
  80. package/dist/packages/sdk/examples/message-routing/routing.test.mjs +27 -0
  81. package/dist/packages/sdk/examples/message-routing/ticket.method +88 -0
  82. package/dist/packages/sdk/examples/message-routing/ticket.test.mjs +64 -0
  83. package/dist/packages/sdk/examples/notes-summary/README.md +23 -0
  84. package/dist/packages/sdk/examples/notes-summary/TASK.md +3 -0
  85. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/case.json +36 -0
  86. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/examples.json +16 -0
  87. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/files/0 +10 -0
  88. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/recording.json +195 -0
  89. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-05-standup.md +4 -0
  90. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-07-planning.md +3 -0
  91. package/dist/packages/sdk/examples/notes-summary/notes-summary.method +54 -0
  92. package/dist/packages/sdk/examples/notes-summary/out/.keep +0 -0
  93. package/dist/packages/sdk/examples/notes-summary/read-notes.mjs +4 -0
  94. package/dist/packages/sdk/examples/notes-summary/save.mjs +7 -0
  95. package/dist/packages/sdk/examples/outbound-management/README.md +119 -0
  96. package/dist/packages/sdk/examples/outbound-management/TASK.md +5 -0
  97. package/dist/packages/sdk/examples/outbound-management/check_plan.py +16 -0
  98. package/dist/packages/sdk/examples/outbound-management/check_sources.py +14 -0
  99. package/dist/packages/sdk/examples/outbound-management/crm.py +153 -0
  100. package/dist/packages/sdk/examples/outbound-management/crm_test.py +141 -0
  101. package/dist/packages/sdk/examples/outbound-management/fixtures/README.md +15 -0
  102. package/dist/packages/sdk/examples/outbound-management/fixtures/crm.json +54 -0
  103. package/dist/packages/sdk/examples/outbound-management/fixtures/day-1/messages.json +29 -0
  104. package/dist/packages/sdk/examples/outbound-management/fixtures/day-2/messages.json +38 -0
  105. package/dist/packages/sdk/examples/outbound-management/fixtures/observations.json +12 -0
  106. package/dist/packages/sdk/examples/outbound-management/fixtures/plan.json +69 -0
  107. package/dist/packages/sdk/examples/outbound-management/fixtures/receipt.json +1 -0
  108. package/dist/packages/sdk/examples/outbound-management/fixtures/state.json +99 -0
  109. package/dist/packages/sdk/examples/outbound-management/fixtures/tasks.md +16 -0
  110. package/dist/packages/sdk/examples/outbound-management/inputs.json +4 -0
  111. package/dist/packages/sdk/examples/outbound-management/outbound.method +277 -0
  112. package/dist/packages/sdk/examples/outbound-management/read_crm.py +4 -0
  113. package/dist/packages/sdk/examples/outbound-management/save_day.py +4 -0
  114. package/dist/packages/sdk/examples/outbound-management/starter/crm.json +11 -0
  115. package/dist/packages/sdk/examples/social-briefing/README.md +41 -0
  116. package/dist/packages/sdk/examples/social-briefing/TASK.md +5 -0
  117. package/dist/packages/sdk/examples/social-briefing/inputs.json +5 -0
  118. package/dist/packages/sdk/examples/social-briefing/result.fixture.json +14 -0
  119. package/dist/packages/sdk/examples/social-briefing/social-briefing.method +110 -0
  120. package/dist/packages/sdk/examples/support-triage/README.md +77 -0
  121. package/dist/packages/sdk/examples/support-triage/TASK.md +3 -0
  122. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/case.json +40 -0
  123. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/recording.json +460 -0
  124. package/dist/packages/sdk/examples/support-triage/fake-helpdesk.mjs +38 -0
  125. package/dist/packages/sdk/examples/support-triage/helpdesk.mjs +33 -0
  126. package/dist/packages/sdk/examples/support-triage/inputs.json +1 -0
  127. package/dist/packages/sdk/examples/support-triage/read-ticket.mjs +8 -0
  128. package/dist/packages/sdk/examples/support-triage/route.mjs +4 -0
  129. package/dist/packages/sdk/examples/support-triage/sample-tickets.json +5 -0
  130. package/dist/packages/sdk/examples/support-triage/set-ticket.mjs +9 -0
  131. package/dist/packages/sdk/examples/support-triage/support-triage.method +165 -0
  132. package/dist/packages/sdk/examples/support-triage/triage.mjs +38 -0
  133. package/dist/packages/sdk/examples/support-triage/triage.test.mjs +68 -0
  134. package/dist/packages/sdk/observers/mail.delivery/fetch.mjs +87 -0
  135. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce-by-recipient.json +1 -0
  136. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce.json +1 -0
  137. package/dist/packages/sdk/observers/mail.delivery/fixtures/delayed.json +1 -0
  138. package/dist/packages/sdk/observers/mail.delivery/fixtures/delivered.json +1 -0
  139. package/dist/packages/sdk/observers/mail.delivery/fixtures/empty.json +1 -0
  140. package/dist/packages/sdk/observers/mail.delivery/fixtures/other-message.json +1 -0
  141. package/dist/packages/sdk/observers/mail.delivery/judge.mjs +13 -0
  142. package/dist/packages/sdk/observers/mail.delivery/observer.json +14 -0
  143. package/dist/packages/sdk/package.json +232 -0
  144. package/dist/packages/sdk/src/authoring-example.d.ts +91 -0
  145. package/dist/packages/sdk/src/authoring-example.js +50 -0
  146. package/dist/packages/sdk/src/authoring-instructions.d.ts +20 -0
  147. package/dist/packages/sdk/src/authoring-instructions.js +151 -0
  148. package/dist/packages/sdk/src/authoring-prompt.d.ts +2 -0
  149. package/dist/packages/sdk/src/authoring-prompt.js +8 -0
  150. package/dist/packages/sdk/src/authoring.d.ts +10 -0
  151. package/dist/packages/sdk/src/authoring.js +287 -0
  152. package/dist/packages/sdk/src/bindings.d.ts +4 -0
  153. package/dist/packages/sdk/src/bindings.js +124 -0
  154. package/dist/packages/sdk/src/bridge.d.ts +2 -0
  155. package/dist/packages/sdk/src/bridge.js +23 -0
  156. package/dist/packages/sdk/src/browser-runtime/pyproject.toml +5 -0
  157. package/dist/packages/sdk/src/browser-runtime/tools.json +189 -0
  158. package/dist/packages/sdk/src/browser-runtime/uv.lock +3894 -0
  159. package/dist/packages/sdk/src/browser-service.py +127 -0
  160. package/dist/packages/sdk/src/browser.d.ts +38 -0
  161. package/dist/packages/sdk/src/browser.js +203 -0
  162. package/dist/packages/sdk/src/capabilities.d.ts +13 -0
  163. package/dist/packages/sdk/src/capabilities.js +68 -0
  164. package/dist/packages/sdk/src/classification-client.d.ts +4 -0
  165. package/dist/packages/sdk/src/classification-client.js +27 -0
  166. package/dist/packages/sdk/src/cloud.d.ts +23 -0
  167. package/dist/packages/sdk/src/cloud.js +43 -0
  168. package/dist/packages/sdk/src/computer-settings.d.ts +28 -0
  169. package/dist/packages/sdk/src/computer-settings.js +57 -0
  170. package/dist/packages/sdk/src/connect.d.ts +47 -0
  171. package/dist/packages/sdk/src/connect.js +203 -0
  172. package/dist/packages/sdk/src/current-inspection.d.ts +3 -0
  173. package/dist/packages/sdk/src/current-inspection.js +146 -0
  174. package/dist/packages/sdk/src/current-runtime.d.ts +14 -0
  175. package/dist/packages/sdk/src/current-runtime.js +167 -0
  176. package/dist/packages/sdk/src/doctor.d.ts +17 -0
  177. package/dist/packages/sdk/src/doctor.js +68 -0
  178. package/dist/packages/sdk/src/executable.d.ts +2 -0
  179. package/dist/packages/sdk/src/executable.js +22 -0
  180. package/dist/packages/sdk/src/explain-effects.d.ts +32 -0
  181. package/dist/packages/sdk/src/explain-effects.js +192 -0
  182. package/dist/packages/sdk/src/explain.d.ts +115 -0
  183. package/dist/packages/sdk/src/explain.js +441 -0
  184. package/dist/packages/sdk/src/files.d.ts +9 -0
  185. package/dist/packages/sdk/src/files.js +50 -0
  186. package/dist/packages/sdk/src/guard-keys.d.ts +5 -0
  187. package/dist/packages/sdk/src/guard-keys.js +42 -0
  188. package/dist/packages/sdk/src/hosted-models.d.ts +49 -0
  189. package/dist/packages/sdk/src/hosted-models.js +81 -0
  190. package/dist/packages/sdk/src/improve.d.ts +55 -0
  191. package/dist/packages/sdk/src/improve.js +159 -0
  192. package/dist/packages/sdk/src/index.d.ts +13 -0
  193. package/dist/packages/sdk/src/index.js +13 -0
  194. package/dist/packages/sdk/src/inspect.d.ts +6 -0
  195. package/dist/packages/sdk/src/inspect.js +17 -0
  196. package/dist/packages/sdk/src/link.d.ts +12 -0
  197. package/dist/packages/sdk/src/link.js +53 -0
  198. package/dist/packages/sdk/src/local-cli.d.ts +24 -0
  199. package/dist/packages/sdk/src/local-cli.js +146 -0
  200. package/dist/packages/sdk/src/local-setup.d.ts +15 -0
  201. package/dist/packages/sdk/src/local-setup.js +42 -0
  202. package/dist/packages/sdk/src/method-client.d.ts +30 -0
  203. package/dist/packages/sdk/src/method-client.js +250 -0
  204. package/dist/packages/sdk/src/method-files.d.ts +29 -0
  205. package/dist/packages/sdk/src/method-files.js +93 -0
  206. package/dist/packages/sdk/src/method-help.d.ts +19 -0
  207. package/dist/packages/sdk/src/method-help.js +404 -0
  208. package/dist/packages/sdk/src/method-issues.d.ts +86 -0
  209. package/dist/packages/sdk/src/method-issues.js +192 -0
  210. package/dist/packages/sdk/src/method-sync.d.ts +46 -0
  211. package/dist/packages/sdk/src/method-sync.js +293 -0
  212. package/dist/packages/sdk/src/method.d.ts +5 -0
  213. package/dist/packages/sdk/src/method.js +359 -0
  214. package/dist/packages/sdk/src/model-checks.d.ts +96 -0
  215. package/dist/packages/sdk/src/model-checks.js +181 -0
  216. package/dist/packages/sdk/src/outbox.d.ts +120 -0
  217. package/dist/packages/sdk/src/outbox.js +297 -0
  218. package/dist/packages/sdk/src/own-model-key.d.ts +22 -0
  219. package/dist/packages/sdk/src/own-model-key.js +76 -0
  220. package/dist/packages/sdk/src/prepare.d.ts +24 -0
  221. package/dist/packages/sdk/src/prepare.js +245 -0
  222. package/dist/packages/sdk/src/process.d.ts +15 -0
  223. package/dist/packages/sdk/src/process.js +107 -0
  224. package/dist/packages/sdk/src/production.d.ts +182 -0
  225. package/dist/packages/sdk/src/production.js +284 -0
  226. package/dist/packages/sdk/src/progress.d.ts +12 -0
  227. package/dist/packages/sdk/src/progress.js +48 -0
  228. package/dist/packages/sdk/src/proposals.d.ts +73 -0
  229. package/dist/packages/sdk/src/proposals.js +284 -0
  230. package/dist/packages/sdk/src/quality.d.ts +82 -0
  231. package/dist/packages/sdk/src/quality.js +294 -0
  232. package/dist/packages/sdk/src/result-files.d.ts +5 -0
  233. package/dist/packages/sdk/src/result-files.js +149 -0
  234. package/dist/packages/sdk/src/result-transfer.d.ts +21 -0
  235. package/dist/packages/sdk/src/result-transfer.js +37 -0
  236. package/dist/packages/sdk/src/run-data.d.ts +11 -0
  237. package/dist/packages/sdk/src/run-data.js +80 -0
  238. package/dist/packages/sdk/src/run-method.d.ts +2 -0
  239. package/dist/packages/sdk/src/run-method.js +2 -0
  240. package/dist/packages/sdk/src/run-saved.d.ts +5 -0
  241. package/dist/packages/sdk/src/run-saved.js +138 -0
  242. package/dist/packages/sdk/src/run-worker.d.ts +6 -0
  243. package/dist/packages/sdk/src/run-worker.js +131 -0
  244. package/dist/packages/sdk/src/secrets.d.ts +37 -0
  245. package/dist/packages/sdk/src/secrets.js +196 -0
  246. package/dist/packages/sdk/src/update.d.ts +5 -0
  247. package/dist/packages/sdk/src/update.js +39 -0
  248. package/dist/packages/sdk/src/versions.d.ts +58 -0
  249. package/dist/packages/sdk/src/versions.js +144 -0
  250. package/dist/packages/sdk/src/worker.d.ts +120 -0
  251. package/dist/packages/sdk/src/worker.js +443 -0
  252. package/dist/packages/workflow-language/src/execution.d.ts +1 -0
  253. package/dist/packages/workflow-language/src/execution.js +1 -0
  254. package/dist/packages/workflow-language/src/inspection.d.ts +990 -0
  255. package/dist/packages/workflow-language/src/inspection.js +130 -0
  256. package/dist/packages/workflow-language/src/result-files.d.ts +160 -0
  257. package/dist/packages/workflow-language/src/result-files.js +67 -0
  258. package/dist/packages/workflow-language/src/schema.d.ts +207 -0
  259. package/dist/packages/workflow-language/src/schema.js +56 -0
  260. package/dist/packages/workflow-language/src/validate.d.ts +11 -0
  261. package/dist/packages/workflow-language/src/validate.js +77 -0
  262. package/package.json +230 -4
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ import packageInfo from '../package.json' with { type: 'json' };
3
+ import runtimeInfo from '@withmethod/runtime/package.json' with { type: 'json' };
4
+ import { startRunWorker, waitForRun, cancelRun, workerAlive } from './run-worker.js';
5
+ import { methodCache } from './prepare.js';
6
+ import { restorePackage } from './method-files.js';
7
+ import { publish } from './versions.js';
8
+ import { runSaved } from './run-saved.js';
9
+ import { bindInput, bindConnection } from './bindings.js';
10
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
11
+ import { join, resolve } from "node:path";
12
+ import { parseArgs } from "node:util";
13
+ import { pathToFileURL } from "node:url";
14
+ import { randomUUID } from "node:crypto";
15
+ import { localAuthoring, authoringPath, readDocument, writeDocument } from "./authoring.js";
16
+ import { MethodClient, DEFAULT_SERVER } from "./method-client.js";
17
+ import { MethodSync } from "./method-sync.js";
18
+ import { progressMain } from "./progress.js";
19
+ import { parse, localMain } from "./local-cli.js";
20
+ import { writePrivateJson } from "./files.js";
21
+ import { loadWorkflow } from "../../workflow-language/src/validate.js";
22
+ import { methodHelp, overviewHelp as help } from "./method-help.js";
23
+ import { fixFor } from "@withmethod/runtime/runner.js";
24
+ /** The next step for a failure before the first step. */
25
+ export function setupFix(error) {
26
+ if (error.code === 'missing_secret') {
27
+ const names = (error.missing ?? []).join(' ');
28
+ return `No step ran. Run method secret find to see which nearby files hold ${names}, then method secret import FILE ${names}; or ask the user to enter each one with method secret set NAME (a private form in their browser). Never open or print a key file, and never ask for a value in chat.`;
29
+ }
30
+ return fixFor(error);
31
+ }
32
+ function safePath(path) {
33
+ return authoringPath(path);
34
+ }
35
+ async function dispatchRunWorker(args, flags, extra = []) {
36
+ if (process.env.METHOD_RUN_WORKER === '1' || (!/[/\\]method\.(?:js|ts)$/.test(process.argv[1] ?? '') && !flags.background))
37
+ return false;
38
+ if (flags.resume && !flags['run-dir'])
39
+ throw Error('Resume needs --run-dir.');
40
+ const directory = safePath(flags['run-dir'] ?? join(methodCache(), 'runs', randomUUID()));
41
+ const workerArgs = [...args.filter(arg => arg !== '--background'), ...extra];
42
+ if (!flags['run-dir'])
43
+ workerArgs.push('--run-dir', directory);
44
+ await startRunWorker(workerArgs, directory, flags.background);
45
+ return true;
46
+ }
47
+ export async function methodMain(args = process.argv.slice(2), clientFactory = server => new MethodClient(server)) {
48
+ const earlyHelp = methodHelp(args);
49
+ if (earlyHelp !== undefined) {
50
+ process.stdout.write(earlyHelp);
51
+ return;
52
+ }
53
+ if (['improve', 'proposals', 'apply'].includes(args[0] ?? ''))
54
+ return (await import('./proposals.js')).proposalCommand(args, clientFactory);
55
+ await (await import('./proposals.js')).autoApply(args, clientFactory);
56
+ if (args[0] === 'worker' || args[0] === 'keys')
57
+ return (await import('./worker.js')).productionCommand(args, clientFactory);
58
+ if (args[0] === '__run-data')
59
+ return (await import('./run-data.js')).runDataCommand(args.slice(1));
60
+ if (args[0] === 'connect' || args[0] === 'answer') {
61
+ const server = parseArgs({ args: args.slice(1), strict: false, options: { server: { type: 'string' } } }).values.server;
62
+ const client = clientFactory(typeof server === 'string' ? server : DEFAULT_SERVER);
63
+ const { connectCommand, answerCommand } = await import('./connect.js');
64
+ process.stdout.write(JSON.stringify(await (args[0] === 'connect' ? connectCommand : answerCommand)(args.slice(1), client), null, 2) + '\n');
65
+ return;
66
+ }
67
+ if (args[0] === 'browser' && args[1] === 'connect') {
68
+ const { configureBrowser } = await import('./browser.js');
69
+ const p = parseArgs({ args: args.slice(2), options: { name: { type: 'string' }, cdp: { type: 'string' } } });
70
+ process.stdout.write(JSON.stringify(configureBrowser(p.values.name ?? 'default', p.values.cdp)) + '\n');
71
+ return;
72
+ }
73
+ if (args[0] === '__worker') {
74
+ const file = args[1];
75
+ const directory = resolve(file, '..');
76
+ writePrivateJson(join(directory, 'worker.json'), { status: 'running', pid: process.pid });
77
+ try {
78
+ await methodMain(JSON.parse(readFileSync(file, 'utf8')).args, clientFactory);
79
+ }
80
+ catch (error) {
81
+ // A run that fails before its first step still prints one result that says why and what to do.
82
+ const failed = { status: error.code === 'needs_input' ? 'needs_input' : 'failed', run_dir: directory, code: error.code ?? 'setup_failed', error: error.message, ...(error.missing ? { missing: error.missing } : {}), fix: setupFix(error) };
83
+ if (!existsSync(join(directory, 'summary.json')))
84
+ writePrivateJson(join(directory, 'summary.json'), failed);
85
+ process.stdout.write(JSON.stringify(failed, null, 2) + '\n');
86
+ process.exitCode = error.code === 'needs_input' ? 2 : 1;
87
+ }
88
+ finally {
89
+ writePrivateJson(join(directory, 'worker.json'), { status: 'finished', pid: process.pid, exit_code: Number(process.exitCode ?? 0) });
90
+ }
91
+ return;
92
+ }
93
+ if (['wait', 'cancel', 'run-status'].includes(args[0] ?? '')) {
94
+ if (!args[1])
95
+ throw Error('Supply the local run directory.');
96
+ const directory = safePath(args[1]);
97
+ const result = args[0] === 'wait' ? await waitForRun(directory) : args[0] === 'cancel' ? cancelRun(directory) : { running: workerAlive(directory), ...(existsSync(join(directory, 'worker.json')) ? readDocument(join(directory, 'worker.json')) : {}) };
98
+ process.stdout.write(JSON.stringify(result) + '\n');
99
+ return;
100
+ }
101
+ if (args[0] === 'secret')
102
+ return (await import('./secrets.js')).secretCommand(args.slice(1));
103
+ if (args[0] === '__checks' && args[1])
104
+ return (await import('./method-issues.js')).checksWorker(args[1]);
105
+ if (args[0] === 'models' && !args.includes('--help'))
106
+ return (await import('./hosted-models.js')).modelsCommand(args.slice(1), clientFactory);
107
+ if (args.length === 1 && args[0] === "--version") {
108
+ process.stdout.write(`Method SDK ${packageInfo.version}; runtime ${runtimeInfo.version}; current format method/3.4\n`);
109
+ return;
110
+ }
111
+ if (["observe", "test", "case", "effect"].includes(args[0] ?? "") && !args.includes("--help")) {
112
+ const quality = await import("./quality.js");
113
+ if (args[0] === "observe")
114
+ return quality.observeCommand(args.slice(1));
115
+ if (args[0] === "test")
116
+ return quality.testCommand(args.slice(1));
117
+ if (args[0] === "case")
118
+ return quality.caseCommand(args.slice(1));
119
+ return quality.effectCommand(args.slice(1));
120
+ }
121
+ if (args[0] === 'run' && /^https?:/.test(args[1] ?? '')) {
122
+ const url = new URL(args[1]);
123
+ const match = url.pathname.match(/^\/methods\/([^/]+)$/);
124
+ if (match) {
125
+ if (url.username || url.password)
126
+ throw Error('Use a Method dashboard URL without credentials.');
127
+ args = ['run', decodeURIComponent(match[1]), ...args.slice(2), '--server', url.origin,
128
+ ...(url.searchParams.get('version') ? ['--version', url.searchParams.get('version')] : [])];
129
+ }
130
+ }
131
+ const localCommand = ["prompt", "inspect", "doctor"].includes(args[0] ?? "")
132
+ || ["run", "steps"].includes(args[0] ?? "") && /\.(method)$|^https?:\/\//i.test(args[1] ?? "");
133
+ if (localCommand) {
134
+ if (args[0] === 'run' && await dispatchRunWorker(args, parse(args).values))
135
+ return;
136
+ return localMain(args);
137
+ }
138
+ const requestedHelp = methodHelp(args);
139
+ if (requestedHelp !== undefined) {
140
+ process.stdout.write(requestedHelp);
141
+ return;
142
+ }
143
+ if (args[0] === "progress")
144
+ return progressMain(args.slice(1));
145
+ if (await localAuthoring(args))
146
+ return;
147
+ // Strip only Method flags. Pass run flags unchanged to the existing SDK parser.
148
+ const methodOptions = new Set([
149
+ "server",
150
+ "version",
151
+ "file",
152
+ "reason",
153
+ "method",
154
+ "connection",
155
+ "accept-failing-case",
156
+ ]);
157
+ const values = {}, rest = [];
158
+ for (let i = 0; i < args.length; i++) {
159
+ const match = args[i].match(/^--([^=]+)(?:=(.*))?$/);
160
+ if (match && methodOptions.has(match[1])) {
161
+ const value = match[2] ?? args[++i];
162
+ if (!value || value.startsWith("--"))
163
+ throw Error(`Supply --${match[1]}.`);
164
+ values[match[1]] = value;
165
+ }
166
+ else
167
+ rest.push(args[i]);
168
+ }
169
+ const [command, target, step] = rest;
170
+ if (!command || command === "--help" || command === "help") {
171
+ process.stdout.write(help);
172
+ return;
173
+ }
174
+ if (command === "sync") {
175
+ if (!target)
176
+ throw Error("Use method sync RUN_DIRECTORY.");
177
+ await MethodSync.retry(safePath(target), values.server ? new MethodClient(values.server) : undefined);
178
+ return;
179
+ }
180
+ const authorFile = command === "publish" || command === "explain" ? target : undefined;
181
+ if (command === "guard-keys")
182
+ return (await import("./guard-keys.js")).guardKeysCommand();
183
+ if (command === "config")
184
+ return (await import("./computer-settings.js")).configCommand(rest.slice(1));
185
+ if (command === "new-id") {
186
+ if (!target || rest.length !== 2)
187
+ throw Error("Use method new-id FILE.");
188
+ const { newMethodId, writeMethodId } = await import("./versions.js");
189
+ const id = newMethodId();
190
+ writeMethodId(safePath(target), id);
191
+ process.stdout.write(JSON.stringify({ file: safePath(target), method_id: id }, null, 2) + "\n");
192
+ return;
193
+ }
194
+ if (!["status", "login", "logout", "devices", "revoke", "list", "get", "steps", "step", "run", "runs", "logs", "publish", "explain", "bind", "state"].includes(command))
195
+ throw Error(help);
196
+ const client = clientFactory(values.server ?? DEFAULT_SERVER);
197
+ if (command === "login") {
198
+ await client.login();
199
+ return;
200
+ }
201
+ if (command === "logout") {
202
+ await client.logout();
203
+ process.stdout.write("Method is disconnected.\n");
204
+ return;
205
+ }
206
+ if (command === "status") {
207
+ let signedIn = false;
208
+ if (client.token()) {
209
+ try {
210
+ await client.request("/api/cli/me");
211
+ signedIn = true;
212
+ }
213
+ catch (error) {
214
+ if (!/^401:/.test(String(error.message)))
215
+ throw error;
216
+ }
217
+ }
218
+ const update = await (await import("./update.js")).updateNotice();
219
+ process.stdout.write(JSON.stringify({ installed: true, server: client.server, signed_in: signedIn, ...(!signedIn ? { next: "Run method login to sign in with browser approval." } : {}), ...(update ? { update } : {}) }, null, 2) + "\n");
220
+ return;
221
+ }
222
+ // Without sign-in, explain prints one line and stops: script cards need a hosted model.
223
+ if (command === "explain") {
224
+ await (await import("./explain.js")).explainCommand(client, rest.slice(1));
225
+ return;
226
+ }
227
+ // The first real command starts browser approval if no credential is saved.
228
+ if (!client.token())
229
+ await client.login();
230
+ // Any signed-in command sends what is left in the outbox, in the background.
231
+ else
232
+ (await import("./outbox.js")).sendLeftovers(client);
233
+ if (command === 'run' && client.token()) {
234
+ try {
235
+ await client.request('/api/cli/me');
236
+ }
237
+ catch (error) {
238
+ if (String(error).includes('401:'))
239
+ await client.login();
240
+ else
241
+ throw error;
242
+ }
243
+ }
244
+ const print = (v) => process.stdout.write(JSON.stringify(v, null, 2) + "\n");
245
+ if (command === "devices") {
246
+ print(await client.request("/api/cli/devices"));
247
+ return;
248
+ }
249
+ if (command === "revoke") {
250
+ if (!target)
251
+ throw Error("Supply a device ID.");
252
+ print(await client.request(`/api/cli/devices/${encodeURIComponent(target)}/revoke`, "POST", {}));
253
+ return;
254
+ }
255
+ if (command === "list") {
256
+ print(await client.request("/api/methods"));
257
+ return;
258
+ }
259
+ if (command === "runs") {
260
+ print(await client.request(`/api/workspace/runs${values.method ? `?workflow_id=${encodeURIComponent(values.method)}` : ""}`));
261
+ return;
262
+ }
263
+ if (command === "logs") {
264
+ if (!target)
265
+ throw Error("Supply a run ID.");
266
+ print(await client.request(`/api/workspace/runs/${encodeURIComponent(target)}`));
267
+ return;
268
+ }
269
+ if (command === 'bind') {
270
+ if (target && step && values.connection) {
271
+ print(await bindConnection(client, target, step, values.connection, rest.includes('--upload')));
272
+ return;
273
+ }
274
+ if (!target || !step || !values.file)
275
+ throw Error('Use method bind ID NAME --file FOLDER [--upload].');
276
+ print(await bindInput(client, target, step, values.file, rest.includes('--upload')));
277
+ return;
278
+ }
279
+ if (command === 'state') {
280
+ if (!target)
281
+ throw Error('Supply a Method ID.');
282
+ const path = `/api/cli/methods/${encodeURIComponent(target)}/state`;
283
+ if (rest.includes('--enable')) {
284
+ if (!values.file)
285
+ throw Error('Supply --file with the initial state JSON.');
286
+ print(await client.request(path, 'POST', { action: 'enable', value: readDocument(values.file) }));
287
+ }
288
+ else if (rest.includes('--release')) {
289
+ const id = rest[rest.indexOf('--release') + 1];
290
+ if (!id)
291
+ throw Error('Supply the stopped run ID after --release. Inspect its actions first.');
292
+ print(await client.request(path, 'POST', { action: 'release', run_id: id }));
293
+ }
294
+ else
295
+ print(await client.request(path));
296
+ return;
297
+ }
298
+ if (command === "publish") {
299
+ if (!authorFile)
300
+ throw Error("Use method publish FILE [--reason TEXT] [--cloud|--workers] [--env NAME].");
301
+ print(await (await import("./cloud.js")).publishWithPlacement(client, rest, () => publish(client, safePath(authorFile), values.reason, (values["accept-failing-case"] ?? "").split(",").map(id => id.trim()).filter(Boolean))));
302
+ return;
303
+ }
304
+ if (!target)
305
+ throw Error(help);
306
+ const path = `/api/cli/methods/${encodeURIComponent(target)}`;
307
+ if (!["get", "steps", "step", "run"].includes(command))
308
+ throw Error(help);
309
+ const saved = await client.request(path +
310
+ (values.version ? `?version=${encodeURIComponent(values.version)}` : ""));
311
+ const workflow = command === "get" ? saved.workflow : loadWorkflow(saved.workflow);
312
+ if (command === "steps") {
313
+ print({ version_id: saved.version_id, steps: workflow.steps });
314
+ return;
315
+ }
316
+ if (command === "step") {
317
+ const found = step ? workflow.steps[step] : undefined;
318
+ if (!found)
319
+ throw Error("Step not found. Use method steps WORKFLOW_ID.");
320
+ print({ version_id: saved.version_id, step: found });
321
+ return;
322
+ }
323
+ if (command === "get") {
324
+ const parsed = parseArgs({
325
+ args: rest.slice(2),
326
+ options: { out: { type: "string" } },
327
+ });
328
+ if (parsed.values.out) {
329
+ const out = safePath(parsed.values.out);
330
+ if (existsSync(out))
331
+ throw Error("Choose a new output file; this file already exists.");
332
+ await restorePackage(saved, resolve(out, ".."), client);
333
+ // The id: line links the file to its Method; its content is the saved version.
334
+ const { methodContent, writeMethodId } = await import("./versions.js");
335
+ writeDocument(out, methodContent(workflow));
336
+ writeMethodId(out, saved.workflow_id);
337
+ (await import("./outbox.js")).recordCheckout(client.server, saved.workflow_id, saved.version_id, saved.version_number, methodContent(workflow));
338
+ print({
339
+ workflow_id: saved.workflow_id,
340
+ version_id: saved.version_id,
341
+ version_number: saved.version_number,
342
+ file: out,
343
+ });
344
+ }
345
+ else
346
+ print({ ...saved, workflow });
347
+ return;
348
+ }
349
+ const flags = parse(rest.slice(2)).values;
350
+ if (await dispatchRunWorker(args, flags, values.version ? [] : ['--version', saved.version_id]))
351
+ return;
352
+ await runSaved(saved, flags, client);
353
+ }
354
+ if (process.argv[1] &&
355
+ import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href)
356
+ methodMain().catch((error) => {
357
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
358
+ process.exitCode = 1;
359
+ });
@@ -0,0 +1,96 @@
1
+ import type { ClassificationProvider } from '@withmethod/runtime';
2
+ export type Level = 'error' | 'warning' | 'note';
3
+ export type Issue = {
4
+ code: string;
5
+ level: Level;
6
+ step?: string;
7
+ field?: string;
8
+ file?: string;
9
+ line?: number;
10
+ message: string;
11
+ fix: string;
12
+ evidence?: {
13
+ probability?: number;
14
+ check?: string;
15
+ run_id?: string;
16
+ };
17
+ accepted?: string;
18
+ };
19
+ type Step = any;
20
+ type Check = {
21
+ /** Change the version when the question or the inputs change; old cached answers then no longer apply. */
22
+ version: number;
23
+ level: Exclude<Level, 'error'>;
24
+ question: string;
25
+ /** Which part of the step the check reads; undefined when the check does not apply to the step. */
26
+ script?: (step: Step) => string | undefined;
27
+ applies: (step: Step) => boolean;
28
+ field: string;
29
+ message: (id: string) => string;
30
+ fix: string;
31
+ };
32
+ export declare const checks: Record<string, Check>;
33
+ /**
34
+ * Checks shown to the user. A check moves here from shadow mode only with a results.json that shows its precision
35
+ * on the frozen held-out set meets its level (warning >= 0.90, note >= 0.75). Run npm run eval:checks first.
36
+ */
37
+ export declare const shownChecks: string[];
38
+ export declare const precisionBar: {
39
+ readonly warning: 0.9;
40
+ readonly note: 0.75;
41
+ };
42
+ /** An answer with a probability of yes at or above this is a finding. */
43
+ export declare const findingThreshold = 0.5;
44
+ /** The step as the check reads it: display text and accepts are left out, because they do not change what it does. */
45
+ export declare function stepText(id: string, step: Step): string;
46
+ export type Target = {
47
+ check: string;
48
+ version: number;
49
+ step: string;
50
+ inputs: Record<string, string>;
51
+ key: string;
52
+ };
53
+ /** The checks that apply to each step, with the exact inputs Jev receives. readText returns a file of the Method folder. */
54
+ export declare function checkTargets(method: any, readText: (path: string) => string | undefined, steps?: Set<string>): Target[];
55
+ export declare const checkKey: (check: string, version: number, inputs: Record<string, string>) => string;
56
+ export type Result = {
57
+ check: string;
58
+ version: number;
59
+ step: string;
60
+ key: string;
61
+ answer: boolean;
62
+ probability: number;
63
+ model: string;
64
+ at: string;
65
+ };
66
+ export declare const checksDir: () => string;
67
+ export declare function cached(key: string): Result | undefined;
68
+ /**
69
+ * One yes/no question to Jev, as the two options yes and no (the form that every Method server accepts). The
70
+ * probability is that of yes.
71
+ */
72
+ export declare const answerOptions: {
73
+ yes: string;
74
+ no: string;
75
+ };
76
+ export declare function askCheck(provider: ClassificationProvider, question: string, inputs: Record<string, string>, signal: AbortSignal): Promise<{
77
+ answer: boolean;
78
+ probability: number;
79
+ model: string;
80
+ }>;
81
+ export declare function evaluateTarget(provider: ClassificationProvider, target: Target, signal: AbortSignal): Promise<Result>;
82
+ /**
83
+ * Run the targets in parallel. Cached answers come at once; new ones get waitMs in all. The others are returned as
84
+ * pending, so the caller can finish them in the background. A failed check is left out and is tried again next time.
85
+ */
86
+ export declare function runChecks(provider: ClassificationProvider | undefined, targets: Target[], waitMs?: number): Promise<{
87
+ done: Result[];
88
+ pending: Target[];
89
+ }>;
90
+ /** Findings of shown checks become issues; shadow checks stay in the cache only. */
91
+ export declare function modelIssues(results: Result[], shown?: string[]): Issue[];
92
+ /** Cached shadow results for a set of keys, for audits. */
93
+ export declare function shadowResults(keys?: Set<string>): Result[];
94
+ /** The per-Method record of each step's text at the last validate, to show warnings only for changed steps. */
95
+ export declare function changedSteps(file: string, method: any, readText: (path: string) => string | undefined): Set<string>;
96
+ export {};
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Model checks: yes/no questions to Jev through the Method account, for steps whose text changed. Each check starts
3
+ * in shadow mode: it is computed and recorded under ~/.cache/method/checks/, never shown. A check is shown only after
4
+ * its precision on the frozen labeled set (testkit/checks) meets its level: warning >= 0.90, note >= 0.75.
5
+ */
6
+ import { createHash, randomUUID } from 'node:crypto';
7
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { serializeWorkflow } from '../../workflow-language/src/validate.js';
10
+ import { methodCache } from './prepare.js';
11
+ const promptKinds = ['call', 'agent'];
12
+ export const checks = {
13
+ script_calls_model: {
14
+ version: 1, level: 'warning', field: 'do',
15
+ question: 'Does this script send text to a language model, an AI model, or an AI classifier (directly or through a helper library or company gateway), and use the model\'s answer? Fetching data, managing API keys, or counting records is not a model call.',
16
+ applies: step => step.do?.kind === 'run', script: step => step.do?.entrypoint,
17
+ message: id => `The script of ${id} appears to call a model.`,
18
+ fix: 'Move each model request into its own call, agent, or classify step with its prompt in the Method, and keep the fixed logic in the script.',
19
+ },
20
+ prompt_multiple_tasks: {
21
+ version: 1, level: 'note', field: 'do.prompt',
22
+ question: 'Does this step\'s prompt ask the model to do two or more separate tasks that could each be their own step with their own output, for example research and then write, or classify and then draft a reply? Several requirements for one output are one task.',
23
+ applies: step => promptKinds.includes(step.do?.kind),
24
+ message: id => `The prompt of ${id} appears to ask for more than one task.`,
25
+ fix: 'Give each task its own step, so that each prompt can be changed and compared alone.',
26
+ },
27
+ check_enforces_wording: {
28
+ version: 1, level: 'note', field: 'check',
29
+ question: 'Does this step\'s check fail an output because it does not use particular words, phrases, or a fixed wording, instead of checking facts, structure, or whether the output meets its goal? Requiring a field, a format such as a date or a link, or a value from the input is not enforcing wording.',
30
+ applies: step => !!step.check?.kind, script: step => step.check?.kind === 'run' ? step.check.entrypoint : undefined,
31
+ message: id => `The check of ${id} appears to require exact wording.`,
32
+ fix: 'Check what the output must contain or achieve, not the words it uses.',
33
+ },
34
+ classify_needs_math: {
35
+ version: 1, level: 'warning', field: 'do.question',
36
+ question: 'Does answering this classification step\'s question need arithmetic, counting, comparing numbers or dates, or another exact calculation that a script does reliably?',
37
+ applies: step => step.do?.kind === 'classify',
38
+ message: id => `The question of ${id} appears to need a calculation.`,
39
+ fix: 'Calculate the value in a run step, and classify only what needs judgment.',
40
+ },
41
+ classify_no_unclear: {
42
+ version: 1, level: 'note', field: 'do.options',
43
+ question: 'Can some inputs of this classification step fit none of its options, or be impossible to decide, while no option covers unclear, other, or none?',
44
+ applies: step => step.do?.kind === 'classify' && !!step.do.options,
45
+ message: id => `The options of ${id} have no choice for an unclear input.`,
46
+ fix: 'Add an option for unclear or other inputs, and decide what a later step does with it.',
47
+ },
48
+ prompt_no_missing_input: {
49
+ version: 1, level: 'warning', field: 'do.prompt',
50
+ question: 'Does this step\'s prompt refer to information, files, or data that are neither written in the prompt nor supplied by the step\'s in: values, so that the model must guess them? Tools, a browser, or a connection named in the step that can get the information count as supplied.',
51
+ applies: step => promptKinds.includes(step.do?.kind),
52
+ message: id => `The prompt of ${id} appears to need information that the step does not give it.`,
53
+ fix: 'Add the missing value to in: from an input or an earlier step, or write it in the prompt.',
54
+ },
55
+ };
56
+ /**
57
+ * Checks shown to the user. A check moves here from shadow mode only with a results.json that shows its precision
58
+ * on the frozen held-out set meets its level (warning >= 0.90, note >= 0.75). Run npm run eval:checks first.
59
+ */
60
+ // Promoted by testkit/checks/results.json: held-out precision 1.00, recall 0.97, with real positives. The other checks stay in shadow mode until real positives clear their bar.
61
+ export const shownChecks = ["script_calls_model"];
62
+ export const precisionBar = { warning: 0.9, note: 0.75 };
63
+ /** An answer with a probability of yes at or above this is a finding. */
64
+ export const findingThreshold = 0.5;
65
+ const limit = (text, max) => text.length > max ? `${text.slice(0, max)}\n[truncated]` : text;
66
+ /** The step as the check reads it: display text and accepts are left out, because they do not change what it does. */
67
+ export function stepText(id, step) {
68
+ const { reading: _reading, accept: _accept, ...rest } = step;
69
+ return limit(serializeWorkflow({ [id]: rest }), 16_000);
70
+ }
71
+ /** The checks that apply to each step, with the exact inputs Jev receives. readText returns a file of the Method folder. */
72
+ export function checkTargets(method, readText, steps) {
73
+ const targets = [];
74
+ for (const [id, step] of Object.entries(method.steps ?? {})) {
75
+ if (steps && !steps.has(id))
76
+ continue;
77
+ for (const [name, check] of Object.entries(checks)) {
78
+ if (!check.applies(step))
79
+ continue;
80
+ const path = check.script?.(step);
81
+ const inputs = { step: stepText(id, step) };
82
+ if (path)
83
+ inputs.script = limit(readText(path) ?? '', 40_000);
84
+ targets.push({ check: name, version: check.version, step: id, inputs, key: checkKey(name, check.version, inputs) });
85
+ }
86
+ }
87
+ return targets;
88
+ }
89
+ export const checkKey = (check, version, inputs) => createHash('sha256').update(JSON.stringify([check, version, inputs.step, inputs.script ?? ''])).digest('hex');
90
+ export const checksDir = () => join(methodCache(), 'checks');
91
+ export function cached(key) {
92
+ const file = join(checksDir(), `${key}.json`);
93
+ try {
94
+ return existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : undefined;
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ function store(result) {
101
+ mkdirSync(checksDir(), { recursive: true, mode: 0o700 });
102
+ const file = join(checksDir(), `${result.key}.json`), temporary = `${file}.${process.pid}.tmp`;
103
+ writeFileSync(temporary, JSON.stringify(result), { mode: 0o600 });
104
+ renameSync(temporary, file);
105
+ }
106
+ /**
107
+ * One yes/no question to Jev, as the two options yes and no (the form that every Method server accepts). The
108
+ * probability is that of yes.
109
+ */
110
+ export const answerOptions = { yes: 'Yes: the question describes this step.', no: 'No: the question does not describe this step.' };
111
+ export async function askCheck(provider, question, inputs, signal) {
112
+ const { model } = await provider.resolve(signal);
113
+ const answer = await provider.evaluate({ request_id: randomUUID(), model, question, options: answerOptions, inputs }, signal);
114
+ const probability = answer.probabilities.yes ?? 0;
115
+ return { answer: probability >= findingThreshold, probability, model };
116
+ }
117
+ export async function evaluateTarget(provider, target, signal) {
118
+ const answer = await askCheck(provider, checks[target.check].question, target.inputs, signal);
119
+ const result = { check: target.check, version: target.version, step: target.step, key: target.key, answer: answer.probability >= findingThreshold,
120
+ probability: answer.probability, model: answer.model, at: new Date().toISOString() };
121
+ store(result);
122
+ return result;
123
+ }
124
+ /**
125
+ * Run the targets in parallel. Cached answers come at once; new ones get waitMs in all. The others are returned as
126
+ * pending, so the caller can finish them in the background. A failed check is left out and is tried again next time.
127
+ */
128
+ export async function runChecks(provider, targets, waitMs = 3000) {
129
+ const done = [], pending = [];
130
+ const open = targets.filter(target => { const hit = cached(target.key); if (hit)
131
+ done.push(hit); return !hit; });
132
+ if (!open.length)
133
+ return { done, pending };
134
+ if (!provider)
135
+ return { done, pending: open };
136
+ const controller = new AbortController();
137
+ const settled = new Set();
138
+ const work = open.map(target => evaluateTarget(provider, target, controller.signal)
139
+ .then(result => { done.push(result); }).catch(() => { }).finally(() => settled.add(target.key)));
140
+ let timer;
141
+ await Promise.race([Promise.all(work), new Promise(resolve => { timer = setTimeout(resolve, waitMs); })]);
142
+ clearTimeout(timer);
143
+ controller.abort();
144
+ pending.push(...open.filter(target => !settled.has(target.key)));
145
+ return { done, pending };
146
+ }
147
+ /** Findings of shown checks become issues; shadow checks stay in the cache only. */
148
+ export function modelIssues(results, shown = shownChecks) {
149
+ return results.filter(result => result.answer && shown.includes(result.check)).map(result => {
150
+ const check = checks[result.check];
151
+ return { code: result.check, level: check.level, step: result.step, field: check.field, message: check.message(result.step), fix: check.fix,
152
+ evidence: { probability: result.probability, check: `${result.check}@${result.version}` } };
153
+ });
154
+ }
155
+ /** Cached shadow results for a set of keys, for audits. */
156
+ export function shadowResults(keys) {
157
+ if (!existsSync(checksDir()))
158
+ return [];
159
+ return readdirSync(checksDir()).filter(name => /^[a-f0-9]{64}\.json$/.test(name) && (!keys || keys.has(name.slice(0, 64))))
160
+ .flatMap(name => { const hit = cached(name.slice(0, 64)); return hit ? [hit] : []; });
161
+ }
162
+ /** The per-Method record of each step's text at the last validate, to show warnings only for changed steps. */
163
+ export function changedSteps(file, method, readText) {
164
+ const dir = join(checksDir(), 'validate'), record = join(dir, `${createHash('sha256').update(file).digest('hex').slice(0, 32)}.json`);
165
+ let before = {};
166
+ try {
167
+ before = existsSync(record) ? JSON.parse(readFileSync(record, 'utf8')).steps ?? {} : {};
168
+ }
169
+ catch { /* A new record. */ }
170
+ const now = {};
171
+ for (const [id, step] of Object.entries(method.steps ?? {})) {
172
+ const scripts = [step.do?.kind === 'run' ? step.do.entrypoint : undefined, step.check?.kind === 'run' ? step.check.entrypoint : undefined]
173
+ .filter(Boolean).map(path => readText(path) ?? '');
174
+ now[id] = createHash('sha256').update(JSON.stringify([stepText(id, step), ...scripts])).digest('hex');
175
+ }
176
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
177
+ const temporary = `${record}.${process.pid}.tmp`;
178
+ writeFileSync(temporary, JSON.stringify({ file, steps: now }), { mode: 0o600 });
179
+ renameSync(temporary, record);
180
+ return new Set(Object.keys(now).filter(id => before[id] !== now[id]));
181
+ }