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

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,47 @@
1
+ import type { MethodClient } from "./method-client.js";
2
+ type Language = "python" | "node";
3
+ export declare function detectLanguage(app: string): Language;
4
+ export declare function connectCommand(args: string[], client: MethodClient): Promise<{
5
+ install: string;
6
+ code: string;
7
+ secrets_to_set_on_the_host: string[];
8
+ next: string[];
9
+ gitignore?: string;
10
+ key: {
11
+ name: string;
12
+ written_to: string;
13
+ note: string;
14
+ kept?: never;
15
+ } | {
16
+ kept: string;
17
+ name?: never;
18
+ written_to?: never;
19
+ note?: never;
20
+ };
21
+ published?: string;
22
+ app: string;
23
+ language: Language;
24
+ method_id: string | undefined;
25
+ published_version_id: string;
26
+ }>;
27
+ /** `method answer RUN_ID [--answer JSON]`: show a production run's question, or answer it. */
28
+ export declare function answerCommand(args: string[], client: MethodClient): Promise<import("./production.js").ProductionRun | {
29
+ run_id: string;
30
+ status: import("./production.js").RunStatus;
31
+ message: string;
32
+ step?: never;
33
+ question?: never;
34
+ form?: never;
35
+ expires_at?: never;
36
+ next?: never;
37
+ } | {
38
+ run_id: string;
39
+ step: string;
40
+ question: string;
41
+ form: unknown;
42
+ expires_at: string | null;
43
+ next: string;
44
+ status?: never;
45
+ message?: never;
46
+ }>;
47
+ export {};
@@ -0,0 +1,203 @@
1
+ import { appendFileSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import { basename, join, relative, resolve } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import { authoringPath, readDocument } from "./authoring.js";
5
+ import { dotenvValues, Method, MethodApiError, ServiceApi } from "./production.js";
6
+ import { open } from "./run-data.js";
7
+ // `method connect [APP_FOLDER]`: connect an app to the Method in this folder. It makes a service key named after the app,
8
+ // writes it to the app's .env without showing it, publishes the Method if it has no published version, and prints the
9
+ // install line and the code for the app's language. `method answer RUN_ID`: answer a production run's question.
10
+ const PRODUCTION_SERVER = "https://app.withmethod.ai";
11
+ /** The one Method file in a folder (not in run folders). */
12
+ function methodFile(folder, given) {
13
+ if (given)
14
+ return authoringPath(given);
15
+ const found = readdirSync(folder).filter(name => /\.method$/i.test(name) && statSync(join(folder, name)).isFile());
16
+ if (found.length === 1)
17
+ return join(folder, found[0]);
18
+ if (!found.length)
19
+ throw Error("No .method file in this folder. Run method connect from the Method's folder, or give --file METHOD_FILE.");
20
+ throw Error(`This folder has more than one Method (${found.join(", ")}). Give --file METHOD_FILE.`);
21
+ }
22
+ export function detectLanguage(app) {
23
+ const has = (name) => existsSync(join(app, name));
24
+ if (has("package.json"))
25
+ return "node";
26
+ if (["pyproject.toml", "requirements.txt", "setup.py", "Pipfile", "uv.lock", "poetry.lock"].some(has))
27
+ return "python";
28
+ if (readdirSync(app).some(name => name.endsWith(".py")))
29
+ return "python";
30
+ if (readdirSync(app).some(name => /\.(m?js|ts)$/.test(name)))
31
+ return "node";
32
+ throw Error(`Cannot tell the language of ${app}: add package.json (Node) or pyproject.toml or requirements.txt (Python).`);
33
+ }
34
+ function installLine(app, language) {
35
+ const has = (name) => existsSync(join(app, name));
36
+ if (language === "node") {
37
+ const pack = process.env.METHOD_NODE_PACKAGE || "@method-ai-hq/sdk";
38
+ return has("pnpm-lock.yaml") ? `pnpm add ${pack}` : has("yarn.lock") ? `yarn add ${pack}` : has("bun.lockb") || has("bun.lock") ? `bun add ${pack}` : `npm install ${pack}`;
39
+ }
40
+ const pack = process.env.METHOD_PYTHON_PACKAGE || "method-ai";
41
+ return has("uv.lock") ? `uv add ${pack}` : has("poetry.lock") ? `poetry add ${pack}` : `pip install ${pack}`;
42
+ }
43
+ function example(document, language, methodId, hasAsk) {
44
+ const inputs = Object.entries(document.inputs ?? {}).filter(([, def]) => !Object.hasOwn(def ?? {}, "default"));
45
+ if (language === "python") {
46
+ const values = inputs.map(([name]) => `"${name}": ${name}`).join(", ");
47
+ return [
48
+ "from withmethod import Method",
49
+ "",
50
+ "method = Method() # reads METHOD_API_KEY from the environment or .env",
51
+ "",
52
+ ...(hasAsk ? [
53
+ "# The Method asks a person before it finishes, so start the run and answer it later.",
54
+ `method.worker.start(methods=["${methodId}"]) # once, when the app starts: runs this Method's runs here`,
55
+ `run_id = method.runs.start("${methodId}", {${values}}, idempotency_key=...)["run_id"] # one key for each event; keep run_id`,
56
+ "",
57
+ "# Where the person decides (for example an approve endpoint):",
58
+ "method.runs.answer(run_id, {...}) # the ask step's out fields",
59
+ 'result = method.runs.wait(run_id)["result"]',
60
+ ] : [
61
+ `run = method.run("${methodId}", {${values}}, idempotency_key=...) # one key for each event`,
62
+ `result = run["result"]`,
63
+ ]),
64
+ ].join("\n");
65
+ }
66
+ const values = inputs.map(([name]) => name).join(", ");
67
+ return [
68
+ 'import { Method } from "@method-ai-hq/sdk";',
69
+ "",
70
+ "const method = new Method(); // reads METHOD_API_KEY from the environment or .env",
71
+ "",
72
+ ...(hasAsk ? [
73
+ "// The Method asks a person before it finishes, so start the run and answer it later.",
74
+ `method.worker.start({ methods: ["${methodId}"] }); // once, when the app starts: runs this Method's runs here`,
75
+ `const { run_id } = await method.runs.start({ method: "${methodId}", inputs: { ${values} }, idempotencyKey: ... }); // keep run_id`,
76
+ "",
77
+ "// Where the person decides (for example an approve endpoint):",
78
+ "await method.runs.answer(run_id, { ... }); // the ask step's out fields",
79
+ "const result = (await method.runs.wait(run_id)).result;",
80
+ ] : [
81
+ `const run = await method.run({ method: "${methodId}", inputs: { ${values} }, idempotencyKey: ... });`,
82
+ "const result = run.result;",
83
+ ]),
84
+ ].join("\n");
85
+ }
86
+ /** Add KEY=VALUE to .env, or replace the line that sets KEY. */
87
+ function writeEnv(file, name, value) {
88
+ const text = existsSync(file) ? readFileSync(file, "utf8") : "";
89
+ const pattern = new RegExp(`^\\s*(?:export\\s+)?${name}\\s*=.*$`, "m");
90
+ if (pattern.test(text))
91
+ writeFileSync(file, text.replace(pattern, `${name}=${value}`), { mode: 0o600 });
92
+ else
93
+ writeFileSync(file, `${text}${text && !text.endsWith("\n") ? "\n" : ""}${name}=${value}\n`, { mode: 0o600 });
94
+ }
95
+ /** Keep .env out of git: add it to the app's .gitignore when the app is in a git repository. */
96
+ function ignoreEnv(app) {
97
+ let folder = resolve(app);
98
+ for (;;) {
99
+ if (existsSync(join(folder, ".git")))
100
+ break;
101
+ const parent = resolve(folder, "..");
102
+ if (parent === folder)
103
+ return null;
104
+ folder = parent;
105
+ }
106
+ const file = join(app, ".gitignore");
107
+ const lines = existsSync(file) ? readFileSync(file, "utf8").split(/\r?\n/).map(line => line.trim()) : [];
108
+ if (lines.some(line => [".env", "/.env", ".env*", "*.env"].includes(line)))
109
+ return null;
110
+ appendFileSync(file, `${lines.length && lines.at(-1) !== "" ? "\n" : ""}.env\n`);
111
+ return relative(process.cwd(), file) || file;
112
+ }
113
+ export async function connectCommand(args, client) {
114
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { file: { type: "string" }, server: { type: "string" } } });
115
+ const app = resolve(positionals[0] ?? ".");
116
+ if (!existsSync(app) || !statSync(app).isDirectory())
117
+ throw Error(`${app} is not a folder. Use method connect APP_FOLDER.`);
118
+ const file = methodFile(process.cwd(), values.file);
119
+ const document = readDocument(file);
120
+ const language = detectLanguage(app);
121
+ if (!client.token())
122
+ await client.login();
123
+ const envFile = join(app, ".env");
124
+ // An app that has a working key keeps it: connect again makes no second key.
125
+ let key = dotenvValues(app).METHOD_API_KEY, keyName = basename(app), madeKey = false;
126
+ if (key) {
127
+ try {
128
+ await new ServiceApi(key, client.server).request("/v1/methods/" + encodeURIComponent(document.id ?? "wf_none"));
129
+ }
130
+ catch (error) {
131
+ if (error instanceof MethodApiError && error.status === 401)
132
+ key = undefined;
133
+ }
134
+ }
135
+ if (!key) {
136
+ const created = await client.request("/api/service-keys", "POST", { name: keyName });
137
+ key = created.key;
138
+ keyName = created.name;
139
+ madeKey = true;
140
+ writeEnv(envFile, "METHOD_API_KEY", key);
141
+ }
142
+ // A server other than the hosted one (a local or test server) goes to .env too, so the app reaches the same server.
143
+ if (client.server !== PRODUCTION_SERVER)
144
+ writeEnv(envFile, "METHOD_SERVER", client.server);
145
+ const ignored = ignoreEnv(app);
146
+ // The app runs the published version. Publish the current file when the Method has none.
147
+ let methodId = document.id, published = null, publishedNow = false;
148
+ if (methodId) {
149
+ try {
150
+ published = (await new ServiceApi(key, client.server).request(`/v1/methods/${encodeURIComponent(methodId)}`)).body.published_version_id;
151
+ }
152
+ catch (error) {
153
+ if (!(error instanceof MethodApiError && error.status === 404))
154
+ throw error;
155
+ }
156
+ }
157
+ if (!published) {
158
+ const { publish } = await import("./versions.js");
159
+ const result = await publish(client, file, `Connected to ${basename(app)}.`);
160
+ methodId = result.workflow_id;
161
+ published = result.version_id;
162
+ publishedNow = true;
163
+ }
164
+ const hasAsk = Object.values(document.steps ?? {}).some((step) => step?.ask);
165
+ const secrets = Object.keys(document.secrets ?? {});
166
+ return {
167
+ app, language, method_id: methodId, published_version_id: published, ...(publishedNow ? { published: "now" } : {}),
168
+ key: madeKey ? { name: keyName, written_to: envFile, note: "The key is in .env only. It is not shown." } : { kept: envFile },
169
+ ...(ignored ? { gitignore: `Added .env to ${ignored}.` } : {}),
170
+ install: installLine(app, language),
171
+ code: example(document, language, methodId, hasAsk),
172
+ secrets_to_set_on_the_host: secrets,
173
+ next: [
174
+ `Run in ${app}: ${installLine(app, language)}`,
175
+ "Add the code where the app handles the event. The first run starts a worker in the app's process; on a server, run `method worker` beside it with the same METHOD_API_KEY if you prefer.",
176
+ ...(secrets.length ? [`Set these secrets where the app runs: ${secrets.join(", ")}. On this computer, method secret set NAME works.`] : []),
177
+ hasAsk ? "Questions of ask steps go to the ask handler; without one, to the dashboard inbox and an email to the person who published the Method." : "",
178
+ "Production runs show on the dashboard under Production.",
179
+ ].filter(Boolean),
180
+ };
181
+ }
182
+ /** `method answer RUN_ID [--answer JSON]`: show a production run's question, or answer it. */
183
+ export async function answerCommand(args, client) {
184
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { answer: { type: "string" }, server: { type: "string" } } });
185
+ const runId = positionals[0];
186
+ if (!runId)
187
+ throw Error("Use method answer RUN_ID [--answer JSON].");
188
+ // A device run's question is sealed with the key that started it: METHOD_API_KEY, the app's .env in this folder, or
189
+ // this computer's sign-in (runs that an app started on this laptop without a key).
190
+ if (!process.env.METHOD_API_KEY && !dotenvValues().METHOD_API_KEY && !client.token())
191
+ await client.login();
192
+ const credential = process.env.METHOD_API_KEY ?? dotenvValues().METHOD_API_KEY ?? client.token();
193
+ const method = new Method({ apiKey: credential, server: client.server, worker: false });
194
+ const run = await method.runs.get(runId);
195
+ if (run.status !== "waiting" || !run.question)
196
+ return { run_id: runId, status: run.status, message: "This run has no question to answer." };
197
+ const question = run.question;
198
+ const asked = question.sealed ? open(credential, "question", run.method_id, question.sealed) : { question: question.question ?? "", form: question.form };
199
+ if (values.answer === undefined)
200
+ return { run_id: runId, step: question.step, question: asked.question, form: asked.form, expires_at: question.expires_at,
201
+ next: `method answer ${runId} --answer '${JSON.stringify(Object.fromEntries(Object.keys(asked.form?.properties ?? {}).map(name => [name, "…"])))}'` };
202
+ return await method.runs.answer(runId, JSON.parse(values.answer));
203
+ }
@@ -0,0 +1,3 @@
1
+ import { type RunInspection } from "../../workflow-language/src/inspection.js";
2
+ /** Convert the public trace into the dashboard's stable inspection contract. */
3
+ export declare function inspectCurrentRun(root: string, activeSnapshot?: boolean, includeFiles?: boolean | "references"): RunInspection;
@@ -0,0 +1,146 @@
1
+ import { documentForDigest } from "@withmethod/runtime/document.js";
2
+ import { attachResultFiles } from "./result-files.js";
3
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
4
+ import { join, sep, basename } from "node:path";
5
+ import { createHash } from "node:crypto";
6
+ import { loadWorkflow } from "../../workflow-language/src/validate.js";
7
+ import { InspectionSchema } from "../../workflow-language/src/inspection.js";
8
+ /** Convert the public trace into the dashboard's stable inspection contract. */
9
+ export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = false) {
10
+ const path = (name) => {
11
+ const actual = realpathSync(join(root, name));
12
+ if (!actual.startsWith(root + sep))
13
+ throw Error("Run record escapes the run directory.");
14
+ return actual;
15
+ };
16
+ const read = (name) => JSON.parse(readFileSync(path(name), "utf8"));
17
+ const rawMethod = read("method.json");
18
+ const workflow = loadWorkflow(rawMethod);
19
+ const manifest = read("manifest.json");
20
+ // The runtime hashes the Method without its id: line.
21
+ const digest = createHash("sha256").update(JSON.stringify(documentForDigest(rawMethod))).digest("hex");
22
+ if (manifest.method_sha256 !== digest)
23
+ throw Error("Saved method hash does not match the run.");
24
+ const active = existsSync(join(root, ".lock"));
25
+ if (active && (!activeSnapshot || readFileSync(path(".lock"), "utf8") !== String(process.pid)))
26
+ throw Error("Finish the run before exporting its records.");
27
+ const summary = read("summary.json");
28
+ const events = readFileSync(path("events.jsonl"), "utf8").split("\n").filter(Boolean).map(line => JSON.parse(line));
29
+ const start = events.find(e => e.event === "run.started");
30
+ const invocations = {};
31
+ const setup = existsSync(join(root, 'setup.json')) ? read('setup.json') : [];
32
+ const runEvents = setup;
33
+ for (const event of events) {
34
+ // Keep useful trace fields; do not upload tool arguments, environment values, or process stdout.
35
+ const recorded = {
36
+ ...(typeof event.provider === 'string' ? { provider: event.provider } : {}),
37
+ ...(typeof event.model === 'string' ? { model: event.model } : {}),
38
+ ...(event.kind === 'classify' ? { kind: 'classify' } : {}),
39
+ ...(typeof event.request_id === 'string' ? { request_id: event.request_id } : {}),
40
+ ...(typeof event.operation_id === 'string' ? { operation_id: event.operation_id } : {}),
41
+ ...(typeof event.confidence === 'number' ? { confidence: event.confidence } : {}),
42
+ ...(event.kind === 'classify' && event.usage !== undefined ? { usage: event.usage } : {}),
43
+ ...(typeof event.duration_ms === 'number' ? { duration_ms: event.duration_ms } : event.event.startsWith('run.') && typeof event.elapsed_ms === 'number' ? { duration_ms: event.elapsed_ms } : {}),
44
+ at: event.at, type: event.event === "human.required" ? "human_input_required" : event.event.replaceAll(".", "_"),
45
+ ...(event.check ? { detail: `${event.check.status}: ${event.check.reason}` } : event.message || event.error ? { detail: String(event.message ?? event.error).slice(0, 4000) } : event.code ? { detail: String(event.code) } : {}),
46
+ ...(Number.isInteger(event.sequence) ? { sequence: event.sequence } : {}),
47
+ ...(["action", "check"].includes(event.phase) ? { phase: event.phase } : {}),
48
+ ...(typeof event.tool === "string" ? { tool: event.tool } : {}),
49
+ ...(typeof event.call_id === "string" ? { call_id: event.call_id } : {}),
50
+ ...(typeof event.child === "string" ? { child: event.child } : {}),
51
+ ...(Number.isSafeInteger(event.completed) && Number.isSafeInteger(event.total) ? { completed: event.completed, total: event.total, ...(typeof event.unit === "string" ? { unit: event.unit } : {}) } : {}),
52
+ ...(event.entrypoint ? { command: [event.runtime, event.entrypoint, ...(event.args ?? [])].filter(Boolean).join(" ") } : typeof event.command === "string" ? { command: event.command } : {}),
53
+ ...(event.exit_code !== undefined ? { exit_code: event.exit_code } : {}),
54
+ ...(typeof event.diagnostics === "string" && event.diagnostics ? { diagnostics: event.diagnostics.length <= 4000 ? event.diagnostics : event.diagnostics.slice(0, 1000) + "\n[middle omitted]\n" + event.diagnostics.slice(-3000) } : {}),
55
+ };
56
+ // Effect verdicts are run events, so the dashboard shows them without a new inspection field.
57
+ if (event.event === "effect.observed")
58
+ recorded.detail = `${event.effect}: ${event.verdict}${event.final ? "" : " (observation continues)"}. ${String(event.reason ?? "")}`.slice(0, 4000);
59
+ if (event.event === "run.status_changed")
60
+ recorded.detail = `Status changed from ${event.from} to ${event.to} after a later observation.`;
61
+ if (event.event === "effects.waived")
62
+ recorded.detail = event.waived.map((w) => `Not observed: ${w.step} (${w.reason})`).join("; ").slice(0, 4000);
63
+ if (event.event === "effects.fixtures_passed")
64
+ recorded.detail = `${event.fixtures.length} effect fixtures gave their expected verdicts.`;
65
+ if (event.event === "run.started" && event.forked_from) {
66
+ const fork = event.forked_from;
67
+ recorded.detail = `Forked from run ${fork.run_dir} (execution ${fork.execution_id}). Reused: ${fork.steps.map((s) => s.step).join(", ")}. `
68
+ + (fork.changed_files.length ? `Changed files: ${fork.changed_files.map((f) => `${f.file} (${f.change})`).join(", ")}. Only the entrypoints of reused steps were compared.` : "No bundle file changed.");
69
+ }
70
+ if (!event.step) {
71
+ runEvents.push(recorded);
72
+ continue;
73
+ }
74
+ if (event.event === "step.imported") {
75
+ // A fork reuses accepted iterations from its parent run; they have no started event here.
76
+ const imported = { ...recorded, detail: `Reused from run ${event.parent_run_dir} (execution ${event.parent_execution_id})` };
77
+ const iterations = event.skipped ? [null] : event.outputs ?? [];
78
+ iterations.forEach((outputs, iteration) => {
79
+ const row = invocations[`${event.step}:${iteration}`] ??= { step_id: event.step, status: "running", checks: [], changes: {}, events: [] };
80
+ row.events.push(imported);
81
+ if (event.skipped)
82
+ row.status = "skipped";
83
+ else {
84
+ row.status = "passed";
85
+ row.outputs = outputs;
86
+ }
87
+ });
88
+ continue;
89
+ }
90
+ const id = `${event.step}:${event.iteration ?? 0}`;
91
+ const row = invocations[id] ??= { step_id: event.step, status: "running", checks: [], changes: {}, events: [] };
92
+ if (event.event === "step.started") {
93
+ row.events = [];
94
+ row.changes = {};
95
+ delete row.prompts;
96
+ delete row.verification;
97
+ delete row.failure;
98
+ delete row.observed_effects;
99
+ }
100
+ row.events.push(recorded);
101
+ if (event.event === "prompt.rendered")
102
+ (row.prompts ??= []).push({ sequence: event.sequence, phase: event.phase, template: event.template, rendered: event.rendered });
103
+ if (event.event === "step.started") {
104
+ row.status = "running";
105
+ row.inputs = event.inputs;
106
+ row.checks = [];
107
+ delete row.outputs;
108
+ delete row.error;
109
+ }
110
+ if (event.event === "step.candidate") {
111
+ const { state, ...outputs } = event.candidate;
112
+ row.outputs = outputs;
113
+ row.status = "returned";
114
+ }
115
+ if (event.event === "check.completed")
116
+ row.checks.push({ id: String(event.sequence), result: event.check.status === "unknown" ? "ambiguous" : event.check.status, summary: event.check.reason, evidence: event.check.evidence, method: typeof workflow.steps[event.step]?.check === "object" && Object.hasOwn(Object(workflow.steps[event.step].check), "kind") ? "executor" : "assert" });
117
+ if (event.event === "step.accepted") {
118
+ row.status = "passed";
119
+ row.outputs = event.outputs;
120
+ row.changes = event.state;
121
+ row.verification = event.check.status === "unchecked" ? "unchecked" : "checked";
122
+ if (event.observed_effects)
123
+ row.observed_effects = event.observed_effects;
124
+ }
125
+ if (event.event === "process.failed" && event.phase === "action" && !event.tool && event.observed_effects)
126
+ row.observed_effects = event.observed_effects;
127
+ if (event.event === "step.skipped")
128
+ row.status = "skipped";
129
+ if (event.event === "human.required")
130
+ row.status = "needs_attention";
131
+ }
132
+ if (!active && summary.status !== "completed")
133
+ for (const row of Object.values(invocations))
134
+ if (["running", "returned"].includes(row.status)) {
135
+ row.status = "needs_attention";
136
+ row.error = summary.error ?? "Run interrupted";
137
+ }
138
+ const files = includeFiles ? attachResultFiles(workflow, invocations, [join(root, "artifacts")], join(root, "artifacts"), includeFiles === "references") : undefined;
139
+ const lastAccepted = events.filter(e => e.event === "step.accepted").at(-1);
140
+ return InspectionSchema.parse({ schema: "workflow-inspection/2", workflow, run_id: basename(root), local_run_directory: root,
141
+ ...(typeof (summary.started_at ?? start?.at) === 'string' ? { started_at: summary.started_at ?? start.at } : {}),
142
+ ...(typeof (summary.device_name ?? start?.device_name) === 'string' ? { device_name: summary.device_name ?? start.device_name } : {}),
143
+ events: runEvents, ...(includeFiles ? { files } : {}), status: active ? "running" : summary.status === "completed" ? "succeeded" : "needs_attention",
144
+ ...(summary.error ? { error: summary.error } : {}), inputs: start?.inputs ?? {}, state: lastAccepted?.state ?? start?.initial_state ?? {},
145
+ resources: Object.fromEntries(Object.entries(start?.config?.environment ?? {}).map(([key, value]) => [key, { description: workflow.environment?.[key]?.description ?? key, path: value }])), invocations });
146
+ }
@@ -0,0 +1,14 @@
1
+ import { MethodClient } from './method-client.js';
2
+ import { runMethod as executeMethod } from "@withmethod/runtime/runner.js";
3
+ import type { MethodSync } from "./method-sync.js";
4
+ import type { parse } from "./local-cli.js";
5
+ export { executeMethod as runCurrentMethod };
6
+ /** Recent run folders on this computer, newest first. The runtime reuses only iterations whose key matches. */
7
+ export declare function recentRuns(exclude: string, limit?: number): string[];
8
+ /**
9
+ * Run a local Method file. The run starts at once: saving and run records go through the outbox and never stop or fail
10
+ * the run. baseConfig is a configuration the SDK made (a saved version's package); otherwise localSetup builds it.
11
+ * runSecrets are values that the SDK gives this run only (such as the signed-in key for improve.method); they come
12
+ * before the shell and the secret store, and are never saved.
13
+ */
14
+ export declare function runCurrentFile(file: string, flags: ReturnType<typeof parse>["values"], syncFactory?: () => MethodSync, onEvent?: (event: any) => Promise<void>, client?: MethodClient, baseConfig?: any, runSecrets?: Record<string, string>): Promise<import("@withmethod/runtime/api-types.js").RunResult>;
@@ -0,0 +1,167 @@
1
+ import { managedClassification } from './classification-client.js';
2
+ import { MethodClient } from './method-client.js';
3
+ import { openBrowser } from './browser.js';
4
+ import { readFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
5
+ import { join, dirname, resolve } from 'node:path';
6
+ import { hostedModelsFor, managedModels } from './hosted-models.js';
7
+ import { resolveSecrets } from './secrets.js';
8
+ import { randomUUID } from 'node:crypto';
9
+ import { assertCheckpointExecutor } from '@withmethod/runtime/executor-version.js';
10
+ import { accountNeeds, checkAgents, resolveAgentProfiles } from './capabilities.js';
11
+ import { prepareRuntime, methodCache } from './prepare.js';
12
+ import { writePrivateJson } from './files.js';
13
+ import { runMethod as executeMethod } from "@withmethod/runtime/runner.js";
14
+ import { localSetup } from "./local-setup.js";
15
+ import { authoringPath } from "./authoring.js";
16
+ export { executeMethod as runCurrentMethod };
17
+ /** Recent run folders on this computer, newest first. The runtime reuses only iterations whose key matches. */
18
+ export function recentRuns(exclude, limit = 20) {
19
+ const roots = [join(methodCache(), 'runs'), join(process.cwd(), '.method-runs'), dirname(resolve(exclude))];
20
+ const found = new Map();
21
+ for (const root of new Set(roots.map(path => resolve(path)))) {
22
+ let names = [];
23
+ try {
24
+ names = readdirSync(root);
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ for (const name of names) {
30
+ const dir = join(root, name);
31
+ if (dir === resolve(exclude))
32
+ continue;
33
+ try {
34
+ found.set(dir, statSync(join(dir, 'checkpoint.json')).mtimeMs);
35
+ }
36
+ catch { /* not a run */ }
37
+ }
38
+ }
39
+ return [...found].sort((a, b) => b[1] - a[1]).slice(0, limit).map(([dir]) => dir);
40
+ }
41
+ /**
42
+ * Run a local Method file. The run starts at once: saving and run records go through the outbox and never stop or fail
43
+ * the run. baseConfig is a configuration the SDK made (a saved version's package); otherwise localSetup builds it.
44
+ * runSecrets are values that the SDK gives this run only (such as the signed-in key for improve.method); they come
45
+ * before the shell and the secret store, and are never saved.
46
+ */
47
+ export async function runCurrentFile(file, flags, syncFactory, onEvent, client = new MethodClient(flags.server), baseConfig, runSecrets = {}) {
48
+ if (flags.resume && !flags['run-dir'])
49
+ throw Error('Resume needs --run-dir.');
50
+ flags = { ...flags, 'run-dir': flags['run-dir'] ?? join(process.cwd(), '.method-runs', randomUUID()) };
51
+ const json = (path) => path ? JSON.parse(readFileSync(authoringPath(path), "utf8")) : undefined;
52
+ if (flags.resume)
53
+ assertCheckpointExecutor(json(join(flags['run-dir'], 'checkpoint.json')));
54
+ const setup = await localSetup(file, flags, baseConfig);
55
+ let config = setup.config;
56
+ const agent = setup.agent;
57
+ const sourceRoot = setup.sourceRoot;
58
+ const method = (await import('./authoring.js')).readDocument(file);
59
+ const controller = new AbortController();
60
+ const stop = () => controller.abort(Object.assign(new Error("Interrupted by operator"), { code: "interrupted" }));
61
+ process.once("SIGINT", stop);
62
+ process.once("SIGTERM", stop);
63
+ let sync;
64
+ let failure;
65
+ let runFailed = false;
66
+ let browser;
67
+ try {
68
+ try {
69
+ sync = syncFactory?.();
70
+ }
71
+ catch (error) {
72
+ process.stderr.write(`Run records stay on this computer: ${error.message}\n`);
73
+ }
74
+ // Records the start; sends nothing. The outbox starts when the run starts.
75
+ await sync?.start(method, json(flags.inputs) ?? {}, {}, false);
76
+ // A run with its own OpenRouter key (config.classification.api_key_env) needs no Method sign-in for classification.
77
+ const needs = accountNeeds(method, config, agent);
78
+ const classification = needs.classification ? managedClassification(client) : undefined;
79
+ // Classification uses the Method account. Hosted models are used when this computer is signed in.
80
+ if (classification && !client.token())
81
+ await client.login();
82
+ const resolvedFile = flags['run-dir'] ? join(authoringPath(flags['run-dir']), 'runtime.resolved.json') : undefined;
83
+ const priorCheckpoint = !!flags.resume && !!resolvedFile && !existsSync(resolvedFile)
84
+ && existsSync(join(dirname(resolvedFile), 'checkpoint.json'));
85
+ let prepared;
86
+ if (priorCheckpoint) {
87
+ // Direct executor runs retain the supplied config without SDK preparation.
88
+ prepared = { config, processPath: process.env.PATH ?? '', prepareBundle: async () => { } };
89
+ }
90
+ else {
91
+ if (flags.resume && resolvedFile && existsSync(resolvedFile))
92
+ config = JSON.parse(readFileSync(resolvedFile, 'utf8'));
93
+ else {
94
+ let hostedModel;
95
+ if (needs.models && client.token()) {
96
+ try {
97
+ hostedModel = await managedModels(client).model();
98
+ }
99
+ catch (error) {
100
+ process.stderr.write(`Hosted models are unavailable (${error.message}). Using a local agent.\n`);
101
+ }
102
+ }
103
+ config.models = await resolveAgentProfiles(method, config, agent, hostedModel);
104
+ if (classification)
105
+ config.classification = await classification.resolve(controller.signal);
106
+ }
107
+ await checkAgents(config.models);
108
+ prepared = await prepareRuntime(sourceRoot, config, method);
109
+ config = prepared.config;
110
+ if (resolvedFile) {
111
+ mkdirSync(dirname(resolvedFile), { recursive: true, mode: 0o700 });
112
+ writePrivateJson(resolvedFile, config);
113
+ }
114
+ if (flags['run-dir']) {
115
+ const path = join(authoringPath(flags['run-dir']), 'setup.json');
116
+ const events = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : [];
117
+ writePrivateJson(path, [...events, { at: new Date().toISOString(), type: 'setup_completed' }]);
118
+ }
119
+ }
120
+ browser = await openBrowser(method, config, flags['run-dir'], controller.signal);
121
+ const hostedModels = Object.values(config.models ?? {}).some((profile) => profile.backend === 'method') ? await hostedModelsFor(client) : undefined;
122
+ const result = await executeMethod(authoringPath(file), config, {
123
+ runDir: flags["run-dir"] ? authoringPath(flags["run-dir"]) : undefined,
124
+ agent: agent, inputs: json(flags.inputs), state: json(flags.state), resume: flags.resume, retry: flags.retry,
125
+ cacheFrom: flags.resume ? [] : recentRuns(authoringPath(flags['run-dir'])),
126
+ fresh: flags.fresh ? true : flags.rerun?.length ? flags.rerun.flatMap(step => step.split(',')).map(step => step.trim()).filter(Boolean) : undefined,
127
+ secrets: { ...resolveSecrets(Object.keys(method.secrets ?? {})), ...Object.fromEntries(Object.entries(runSecrets).filter(([key]) => Object.hasOwn(method.secrets ?? {}, key))) }, ...(hostedModels ? { hostedModels } : {}),
128
+ human: json(flags.human), signal: controller.signal,
129
+ ...(browser ? { connections: browser.connections } : {}),
130
+ sourceRoot, ...(classification ? { classification } : {}),
131
+ processPath: prepared.processPath,
132
+ prepareBundle: prepared.prepareBundle,
133
+ onStart: async ({ method, inputs }) => {
134
+ // A configured binding that the Method does not declare is not one of its connections.
135
+ await sync?.start(method, inputs, Object.fromEntries(Object.entries(config.environment ?? {}).filter(([key]) => method.environment?.[key]).map(([key, path]) => [key, { description: method.environment[key].description, path: String(path) }])));
136
+ },
137
+ onEvent: async (event) => { await onEvent?.(event); sync?.snapshot(); if (flags.verbose)
138
+ process.stderr.write(JSON.stringify(event) + "\n"); },
139
+ });
140
+ runFailed = !['completed', 'unconfirmed'].includes(result.status);
141
+ const synced = await sync?.finish();
142
+ const display = synced ? { ...result, dashboard_sync: synced } : result;
143
+ process.stdout.write(JSON.stringify(display, null, 2) + "\n");
144
+ // 3: every step finished, but an observer could not confirm an external change.
145
+ if (result.status !== "completed")
146
+ process.exitCode = { needs_input: 2, unconfirmed: 3 }[result.status] ?? 1;
147
+ return result;
148
+ }
149
+ catch (error) {
150
+ failure = error;
151
+ throw error;
152
+ }
153
+ finally {
154
+ try {
155
+ await browser?.close();
156
+ }
157
+ catch (error) {
158
+ if (!failure && !runFailed && !controller.signal.aborted)
159
+ throw error;
160
+ }
161
+ finally {
162
+ process.removeListener("SIGINT", stop);
163
+ process.removeListener("SIGTERM", stop);
164
+ await sync?.finish(failure);
165
+ }
166
+ }
167
+ }
@@ -0,0 +1,17 @@
1
+ export declare const MINIMUM_CODEX_VERSION = "0.153.0";
2
+ export declare const MINIMUM_NODE_MAJOR = 22;
3
+ export type Finding = {
4
+ ok: true;
5
+ detail: string;
6
+ } | {
7
+ ok: false;
8
+ detail: string;
9
+ };
10
+ export declare function parseVersion(text: string): string | null;
11
+ /** Negative when left is older than right. */
12
+ export declare function compareVersions(left: string, right: string): number;
13
+ export declare function checkNode(version?: string): Finding;
14
+ /** Requires codex ${MINIMUM_CODEX_VERSION}+ with `codex exec --dangerously-bypass-approvals-and-sandbox`. */
15
+ export declare function checkCodex(command?: string): Promise<Finding>;
16
+ /** The default run directory root must accept a private file. */
17
+ export declare function checkRunDirectory(root: string): Finding;