@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.
- package/LICENSE +21 -0
- package/README.md +23 -2
- package/dist/packages/contracts/src/identity.d.ts +16 -0
- package/dist/packages/contracts/src/identity.js +41 -0
- package/dist/packages/contracts/src/method-package.d.ts +71 -0
- package/dist/packages/contracts/src/method-package.js +22 -0
- package/dist/packages/method-document/src/conversion-report.d.ts +138 -0
- package/dist/packages/method-document/src/conversion-report.js +23 -0
- package/dist/packages/sdk/examples/daily-briefing/README.md +31 -0
- package/dist/packages/sdk/examples/daily-briefing/TASK.md +3 -0
- package/dist/packages/sdk/examples/daily-briefing/approved-report.md +138 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_artifacts.py +39 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_check_inputs.py +47 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_files.py +31 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_manifest.py +23 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_markdown.py +67 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_progress.py +18 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_render.py +71 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_sessions.py +37 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_times.py +158 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_validation.py +101 -0
- package/dist/packages/sdk/examples/daily-briefing/briefing_website.py +215 -0
- package/dist/packages/sdk/examples/daily-briefing/checks.json +19 -0
- package/dist/packages/sdk/examples/daily-briefing/daily-briefing.method +158 -0
- package/dist/packages/sdk/examples/daily-briefing/inputs.json +1 -0
- package/dist/packages/sdk/examples/daily-briefing/package-lock.json +10 -0
- package/dist/packages/sdk/examples/daily-briefing/package.json +4 -0
- package/dist/packages/sdk/examples/daily-briefing/pyproject.toml +8 -0
- package/dist/packages/sdk/examples/daily-briefing/reader/reader.css +35 -0
- package/dist/packages/sdk/examples/daily-briefing/reader/reader.js +42 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/context.json +1 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/documents/document-001.md +219 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/media.json +1 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/metadata/documents.json +39 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0001.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0002.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0003.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0004.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0005.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0006.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0007.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0008.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0009.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0010.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0011.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0012.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0013.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0014.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0015.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0016.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0017.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0018.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0019.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0020.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0021.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0022.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0023.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0024.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0025.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0026.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0027.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0028.json +49 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/sessions.json +1 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/timeline.jsonl +28 -0
- package/dist/packages/sdk/examples/daily-briefing/sample/untimed.jsonl +0 -0
- package/dist/packages/sdk/examples/daily-briefing/sample-output.zip +0 -0
- package/dist/packages/sdk/examples/daily-briefing/sample-report.md +51 -0
- package/dist/packages/sdk/examples/daily-briefing/uv.lock +128 -0
- package/dist/packages/sdk/examples/daily-briefing/vendor/marked-LICENSE.md +44 -0
- package/dist/packages/sdk/examples/daily-briefing/vendor/marked.mjs +77 -0
- package/dist/packages/sdk/examples/files.json +126 -0
- package/dist/packages/sdk/examples/message-routing/README.md +23 -0
- package/dist/packages/sdk/examples/message-routing/TASK.md +5 -0
- package/dist/packages/sdk/examples/message-routing/choose-destination.mjs +10 -0
- package/dist/packages/sdk/examples/message-routing/create-ticket.mjs +15 -0
- package/dist/packages/sdk/examples/message-routing/inputs.json +1 -0
- package/dist/packages/sdk/examples/message-routing/message-routing.method +45 -0
- package/dist/packages/sdk/examples/message-routing/result.fixture.json +6 -0
- package/dist/packages/sdk/examples/message-routing/routing.mjs +29 -0
- package/dist/packages/sdk/examples/message-routing/routing.test.mjs +27 -0
- package/dist/packages/sdk/examples/message-routing/ticket.method +88 -0
- package/dist/packages/sdk/examples/message-routing/ticket.test.mjs +64 -0
- package/dist/packages/sdk/examples/notes-summary/README.md +23 -0
- package/dist/packages/sdk/examples/notes-summary/TASK.md +3 -0
- package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/case.json +36 -0
- package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/examples.json +16 -0
- package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/files/0 +10 -0
- package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/recording.json +195 -0
- package/dist/packages/sdk/examples/notes-summary/notes/2026-10-05-standup.md +4 -0
- package/dist/packages/sdk/examples/notes-summary/notes/2026-10-07-planning.md +3 -0
- package/dist/packages/sdk/examples/notes-summary/notes-summary.method +54 -0
- package/dist/packages/sdk/examples/notes-summary/out/.keep +0 -0
- package/dist/packages/sdk/examples/notes-summary/read-notes.mjs +4 -0
- package/dist/packages/sdk/examples/notes-summary/save.mjs +7 -0
- package/dist/packages/sdk/examples/outbound-management/README.md +119 -0
- package/dist/packages/sdk/examples/outbound-management/TASK.md +5 -0
- package/dist/packages/sdk/examples/outbound-management/check_plan.py +16 -0
- package/dist/packages/sdk/examples/outbound-management/check_sources.py +14 -0
- package/dist/packages/sdk/examples/outbound-management/crm.py +153 -0
- package/dist/packages/sdk/examples/outbound-management/crm_test.py +141 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/README.md +15 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/crm.json +54 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/day-1/messages.json +29 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/day-2/messages.json +38 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/observations.json +12 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/plan.json +69 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/receipt.json +1 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/state.json +99 -0
- package/dist/packages/sdk/examples/outbound-management/fixtures/tasks.md +16 -0
- package/dist/packages/sdk/examples/outbound-management/inputs.json +4 -0
- package/dist/packages/sdk/examples/outbound-management/outbound.method +277 -0
- package/dist/packages/sdk/examples/outbound-management/read_crm.py +4 -0
- package/dist/packages/sdk/examples/outbound-management/save_day.py +4 -0
- package/dist/packages/sdk/examples/outbound-management/starter/crm.json +11 -0
- package/dist/packages/sdk/examples/social-briefing/README.md +41 -0
- package/dist/packages/sdk/examples/social-briefing/TASK.md +5 -0
- package/dist/packages/sdk/examples/social-briefing/inputs.json +5 -0
- package/dist/packages/sdk/examples/social-briefing/result.fixture.json +14 -0
- package/dist/packages/sdk/examples/social-briefing/social-briefing.method +110 -0
- package/dist/packages/sdk/examples/support-triage/README.md +77 -0
- package/dist/packages/sdk/examples/support-triage/TASK.md +3 -0
- package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/case.json +40 -0
- package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/recording.json +460 -0
- package/dist/packages/sdk/examples/support-triage/fake-helpdesk.mjs +38 -0
- package/dist/packages/sdk/examples/support-triage/helpdesk.mjs +33 -0
- package/dist/packages/sdk/examples/support-triage/inputs.json +1 -0
- package/dist/packages/sdk/examples/support-triage/read-ticket.mjs +8 -0
- package/dist/packages/sdk/examples/support-triage/route.mjs +4 -0
- package/dist/packages/sdk/examples/support-triage/sample-tickets.json +5 -0
- package/dist/packages/sdk/examples/support-triage/set-ticket.mjs +9 -0
- package/dist/packages/sdk/examples/support-triage/support-triage.method +165 -0
- package/dist/packages/sdk/examples/support-triage/triage.mjs +38 -0
- package/dist/packages/sdk/examples/support-triage/triage.test.mjs +68 -0
- package/dist/packages/sdk/observers/mail.delivery/fetch.mjs +87 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce-by-recipient.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/delayed.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/delivered.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/empty.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/fixtures/other-message.json +1 -0
- package/dist/packages/sdk/observers/mail.delivery/judge.mjs +13 -0
- package/dist/packages/sdk/observers/mail.delivery/observer.json +14 -0
- package/dist/packages/sdk/package.json +232 -0
- package/dist/packages/sdk/src/authoring-example.d.ts +91 -0
- package/dist/packages/sdk/src/authoring-example.js +50 -0
- package/dist/packages/sdk/src/authoring-instructions.d.ts +20 -0
- package/dist/packages/sdk/src/authoring-instructions.js +151 -0
- package/dist/packages/sdk/src/authoring-prompt.d.ts +2 -0
- package/dist/packages/sdk/src/authoring-prompt.js +8 -0
- package/dist/packages/sdk/src/authoring.d.ts +10 -0
- package/dist/packages/sdk/src/authoring.js +287 -0
- package/dist/packages/sdk/src/bindings.d.ts +4 -0
- package/dist/packages/sdk/src/bindings.js +124 -0
- package/dist/packages/sdk/src/bridge.d.ts +2 -0
- package/dist/packages/sdk/src/bridge.js +23 -0
- package/dist/packages/sdk/src/browser-runtime/pyproject.toml +5 -0
- package/dist/packages/sdk/src/browser-runtime/tools.json +189 -0
- package/dist/packages/sdk/src/browser-runtime/uv.lock +3894 -0
- package/dist/packages/sdk/src/browser-service.py +127 -0
- package/dist/packages/sdk/src/browser.d.ts +38 -0
- package/dist/packages/sdk/src/browser.js +203 -0
- package/dist/packages/sdk/src/capabilities.d.ts +13 -0
- package/dist/packages/sdk/src/capabilities.js +68 -0
- package/dist/packages/sdk/src/classification-client.d.ts +4 -0
- package/dist/packages/sdk/src/classification-client.js +27 -0
- package/dist/packages/sdk/src/cloud.d.ts +23 -0
- package/dist/packages/sdk/src/cloud.js +43 -0
- package/dist/packages/sdk/src/computer-settings.d.ts +28 -0
- package/dist/packages/sdk/src/computer-settings.js +57 -0
- package/dist/packages/sdk/src/connect.d.ts +47 -0
- package/dist/packages/sdk/src/connect.js +203 -0
- package/dist/packages/sdk/src/current-inspection.d.ts +3 -0
- package/dist/packages/sdk/src/current-inspection.js +146 -0
- package/dist/packages/sdk/src/current-runtime.d.ts +14 -0
- package/dist/packages/sdk/src/current-runtime.js +167 -0
- package/dist/packages/sdk/src/doctor.d.ts +17 -0
- package/dist/packages/sdk/src/doctor.js +68 -0
- package/dist/packages/sdk/src/executable.d.ts +2 -0
- package/dist/packages/sdk/src/executable.js +22 -0
- package/dist/packages/sdk/src/explain-effects.d.ts +32 -0
- package/dist/packages/sdk/src/explain-effects.js +192 -0
- package/dist/packages/sdk/src/explain.d.ts +115 -0
- package/dist/packages/sdk/src/explain.js +441 -0
- package/dist/packages/sdk/src/files.d.ts +9 -0
- package/dist/packages/sdk/src/files.js +50 -0
- package/dist/packages/sdk/src/guard-keys.d.ts +5 -0
- package/dist/packages/sdk/src/guard-keys.js +42 -0
- package/dist/packages/sdk/src/hosted-models.d.ts +49 -0
- package/dist/packages/sdk/src/hosted-models.js +81 -0
- package/dist/packages/sdk/src/improve.d.ts +55 -0
- package/dist/packages/sdk/src/improve.js +159 -0
- package/dist/packages/sdk/src/index.d.ts +13 -0
- package/dist/packages/sdk/src/index.js +13 -0
- package/dist/packages/sdk/src/inspect.d.ts +6 -0
- package/dist/packages/sdk/src/inspect.js +17 -0
- package/dist/packages/sdk/src/link.d.ts +12 -0
- package/dist/packages/sdk/src/link.js +53 -0
- package/dist/packages/sdk/src/local-cli.d.ts +24 -0
- package/dist/packages/sdk/src/local-cli.js +146 -0
- package/dist/packages/sdk/src/local-setup.d.ts +15 -0
- package/dist/packages/sdk/src/local-setup.js +42 -0
- package/dist/packages/sdk/src/method-client.d.ts +30 -0
- package/dist/packages/sdk/src/method-client.js +250 -0
- package/dist/packages/sdk/src/method-files.d.ts +29 -0
- package/dist/packages/sdk/src/method-files.js +93 -0
- package/dist/packages/sdk/src/method-help.d.ts +19 -0
- package/dist/packages/sdk/src/method-help.js +404 -0
- package/dist/packages/sdk/src/method-issues.d.ts +86 -0
- package/dist/packages/sdk/src/method-issues.js +192 -0
- package/dist/packages/sdk/src/method-sync.d.ts +46 -0
- package/dist/packages/sdk/src/method-sync.js +293 -0
- package/dist/packages/sdk/src/method.d.ts +5 -0
- package/dist/packages/sdk/src/method.js +359 -0
- package/dist/packages/sdk/src/model-checks.d.ts +96 -0
- package/dist/packages/sdk/src/model-checks.js +181 -0
- package/dist/packages/sdk/src/outbox.d.ts +120 -0
- package/dist/packages/sdk/src/outbox.js +297 -0
- package/dist/packages/sdk/src/own-model-key.d.ts +22 -0
- package/dist/packages/sdk/src/own-model-key.js +76 -0
- package/dist/packages/sdk/src/prepare.d.ts +24 -0
- package/dist/packages/sdk/src/prepare.js +245 -0
- package/dist/packages/sdk/src/process.d.ts +15 -0
- package/dist/packages/sdk/src/process.js +107 -0
- package/dist/packages/sdk/src/production.d.ts +182 -0
- package/dist/packages/sdk/src/production.js +284 -0
- package/dist/packages/sdk/src/progress.d.ts +12 -0
- package/dist/packages/sdk/src/progress.js +48 -0
- package/dist/packages/sdk/src/proposals.d.ts +73 -0
- package/dist/packages/sdk/src/proposals.js +284 -0
- package/dist/packages/sdk/src/quality.d.ts +82 -0
- package/dist/packages/sdk/src/quality.js +294 -0
- package/dist/packages/sdk/src/result-files.d.ts +5 -0
- package/dist/packages/sdk/src/result-files.js +149 -0
- package/dist/packages/sdk/src/result-transfer.d.ts +21 -0
- package/dist/packages/sdk/src/result-transfer.js +37 -0
- package/dist/packages/sdk/src/run-data.d.ts +11 -0
- package/dist/packages/sdk/src/run-data.js +80 -0
- package/dist/packages/sdk/src/run-method.d.ts +2 -0
- package/dist/packages/sdk/src/run-method.js +2 -0
- package/dist/packages/sdk/src/run-saved.d.ts +5 -0
- package/dist/packages/sdk/src/run-saved.js +138 -0
- package/dist/packages/sdk/src/run-worker.d.ts +6 -0
- package/dist/packages/sdk/src/run-worker.js +131 -0
- package/dist/packages/sdk/src/secrets.d.ts +37 -0
- package/dist/packages/sdk/src/secrets.js +196 -0
- package/dist/packages/sdk/src/update.d.ts +5 -0
- package/dist/packages/sdk/src/update.js +39 -0
- package/dist/packages/sdk/src/versions.d.ts +58 -0
- package/dist/packages/sdk/src/versions.js +144 -0
- package/dist/packages/sdk/src/worker.d.ts +120 -0
- package/dist/packages/sdk/src/worker.js +443 -0
- package/dist/packages/workflow-language/src/execution.d.ts +1 -0
- package/dist/packages/workflow-language/src/execution.js +1 -0
- package/dist/packages/workflow-language/src/inspection.d.ts +990 -0
- package/dist/packages/workflow-language/src/inspection.js +130 -0
- package/dist/packages/workflow-language/src/result-files.d.ts +160 -0
- package/dist/packages/workflow-language/src/result-files.js +67 -0
- package/dist/packages/workflow-language/src/schema.d.ts +207 -0
- package/dist/packages/workflow-language/src/schema.js +56 -0
- package/dist/packages/workflow-language/src/validate.d.ts +11 -0
- package/dist/packages/workflow-language/src/validate.js +77 -0
- package/package.json +230 -4
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, renameSync, cpSync, openSync, closeSync, symlinkSync, readdirSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, delimiter, resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { execFile } from 'node:child_process';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
const exec = promisify(execFile);
|
|
10
|
+
export const methodCache = () => process.env.METHOD_CACHE_DIR ?? join(homedir(), '.cache', 'method');
|
|
11
|
+
export const runFolderDays = 30;
|
|
12
|
+
/**
|
|
13
|
+
* Delete the run folders that the SDK keeps under the cache when nothing in them changed for 30 days and the account
|
|
14
|
+
* has the run. A folder is kept when it is the only copy of the run: a device-only run, a run that was never sent, or
|
|
15
|
+
* a run that still waits in the outbox. A project's own .method-runs folder is not touched. A folder that cannot be
|
|
16
|
+
* read or removed is left as it is.
|
|
17
|
+
*/
|
|
18
|
+
export function pruneCachedRuns(root = join(methodCache(), 'runs'), now = Date.now()) {
|
|
19
|
+
const cutoff = now - runFolderDays * 86_400_000;
|
|
20
|
+
let entries;
|
|
21
|
+
try {
|
|
22
|
+
entries = readdirSync(root);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let waiting;
|
|
28
|
+
const unsent = (folder) => {
|
|
29
|
+
if (!waiting) {
|
|
30
|
+
waiting = new Set();
|
|
31
|
+
const outbox = join(methodCache(), 'outbox');
|
|
32
|
+
let names = [];
|
|
33
|
+
try {
|
|
34
|
+
names = readdirSync(outbox).filter(name => name.endsWith('.json'));
|
|
35
|
+
}
|
|
36
|
+
catch { /* An empty outbox. */ }
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
try {
|
|
39
|
+
const item = JSON.parse(readFileSync(join(outbox, name), 'utf8'));
|
|
40
|
+
if (item?.kind === 'run')
|
|
41
|
+
waiting.add(resolve(item.run_dir));
|
|
42
|
+
}
|
|
43
|
+
catch { /* Unreadable: a later command removes or sends it. */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return waiting.has(resolve(folder));
|
|
47
|
+
};
|
|
48
|
+
for (const name of entries) {
|
|
49
|
+
const folder = join(root, name);
|
|
50
|
+
try {
|
|
51
|
+
const info = lstatSync(folder);
|
|
52
|
+
if (!info.isDirectory())
|
|
53
|
+
continue;
|
|
54
|
+
const sync = JSON.parse(readFileSync(join(folder, 'method-sync.json'), 'utf8'));
|
|
55
|
+
if (sync.device === true || !sync.dashboard_id || unsent(folder))
|
|
56
|
+
continue;
|
|
57
|
+
const latest = Math.max(info.mtimeMs, ...readdirSync(folder).map(entry => lstatSync(join(folder, entry)).mtimeMs));
|
|
58
|
+
if (latest < cutoff)
|
|
59
|
+
rmSync(folder, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
catch { /* Left for a later run. */ }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A folder with a `method` command that runs this CLI. The SDK puts it first on the PATH that scripts get, so a script
|
|
66
|
+
* that runs `method` (such as improve.method's `method test`) uses the CLI that runs the Method, not an older one.
|
|
67
|
+
*/
|
|
68
|
+
export function cliBin() {
|
|
69
|
+
const source = import.meta.url.endsWith('.ts');
|
|
70
|
+
const entry = fileURLToPath(new URL(`./method.${source ? 'ts' : 'js'}`, import.meta.url));
|
|
71
|
+
// From source (tests and development), tsx loads the TypeScript; its absolute URL works from any folder.
|
|
72
|
+
const argv = [process.execPath, ...(source ? ['--import', pathToFileURL(createRequire(import.meta.url).resolve('tsx')).href] : []), entry];
|
|
73
|
+
const quote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
74
|
+
const script = `#!/bin/sh\nexec ${argv.map(quote).join(' ')} "$@"\n`;
|
|
75
|
+
const folder = join(methodCache(), 'cli', createHash('sha256').update(script).digest('hex').slice(0, 16));
|
|
76
|
+
const file = join(folder, 'method');
|
|
77
|
+
if (!existsSync(file) || readFileSync(file, 'utf8') !== script) {
|
|
78
|
+
mkdirSync(folder, { recursive: true, mode: 0o700 });
|
|
79
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
80
|
+
writeFileSync(temporary, script, { mode: 0o700 });
|
|
81
|
+
renameSync(temporary, file);
|
|
82
|
+
}
|
|
83
|
+
return folder;
|
|
84
|
+
}
|
|
85
|
+
const uvVersion = '0.8.22';
|
|
86
|
+
export async function command(program, args, cwd, env = process.env) {
|
|
87
|
+
try {
|
|
88
|
+
return await exec(program, args, { cwd, env, maxBuffer: 8_000_000, timeout: 600_000 });
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
throw Error(`${program} failed: ${e.stderr || e.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function uv() {
|
|
95
|
+
const platform = process.platform === 'darwin' ? 'apple-darwin' : process.platform === 'linux' ? 'unknown-linux-gnu' : undefined;
|
|
96
|
+
if (!platform || !['arm64', 'x64'].includes(process.arch))
|
|
97
|
+
throw Error('Managed Python supports macOS and glibc Linux on arm64 and x64. Supply a custom runtime on this system.');
|
|
98
|
+
const target = `${process.arch === 'arm64' ? 'aarch64' : 'x86_64'}-${platform}`;
|
|
99
|
+
const root = join(methodCache(), 'tools', `uv-${uvVersion}-${target}`), bin = join(root, `uv-${target}`, 'uv');
|
|
100
|
+
if (existsSync(bin))
|
|
101
|
+
return bin;
|
|
102
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
103
|
+
const name = `uv-${target}.tar.gz`, url = `https://github.com/astral-sh/uv/releases/download/${uvVersion}/${name}`;
|
|
104
|
+
const [archive, check] = await Promise.all([fetch(url), fetch(`${url}.sha256`)]);
|
|
105
|
+
if (!archive.ok || !check.ok)
|
|
106
|
+
throw Error('Could not download the managed Python installer. Retry this run.');
|
|
107
|
+
const data = Buffer.from(await archive.arrayBuffer()), expected = (await check.text()).trim().split(/\s+/)[0];
|
|
108
|
+
if (createHash('sha256').update(data).digest('hex') !== expected)
|
|
109
|
+
throw Error('Python installer checksum failed.');
|
|
110
|
+
const tmp = join(root, `download-${process.pid}`);
|
|
111
|
+
mkdirSync(tmp, { recursive: true });
|
|
112
|
+
writeFileSync(join(tmp, name), data);
|
|
113
|
+
await command('tar', ['-xzf', join(tmp, name), '-C', tmp], root);
|
|
114
|
+
try {
|
|
115
|
+
renameSync(join(tmp, `uv-${target}`), join(root, `uv-${target}`));
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
if (!existsSync(bin))
|
|
119
|
+
throw e;
|
|
120
|
+
}
|
|
121
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
122
|
+
return bin;
|
|
123
|
+
}
|
|
124
|
+
async function npm(root, args) {
|
|
125
|
+
const bundled = resolve(dirname(process.execPath), '../lib/node_modules/npm/bin/npm-cli.js');
|
|
126
|
+
if (existsSync(bundled))
|
|
127
|
+
return command(process.execPath, [bundled, ...args], root);
|
|
128
|
+
return command(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, root);
|
|
129
|
+
}
|
|
130
|
+
export async function lockDependencies(root) {
|
|
131
|
+
if (existsSync(join(root, 'package.json')) && !existsSync(join(root, 'package-lock.json')))
|
|
132
|
+
await npm(root, ['install', '--package-lock-only', '--ignore-scripts', '--no-audit', '--no-fund']);
|
|
133
|
+
if (existsSync(join(root, 'pyproject.toml')) && !existsSync(join(root, 'uv.lock')))
|
|
134
|
+
await command(await uv(), ['lock', '--project', root], root);
|
|
135
|
+
}
|
|
136
|
+
export async function prepareRuntime(root, config, method) {
|
|
137
|
+
for (let attempt = 0;; attempt++)
|
|
138
|
+
try {
|
|
139
|
+
return await prepareOnce(root, config, method);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (attempt || error.code !== 'cache_damaged')
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function prepareOnce(root, config, method) {
|
|
147
|
+
const files = ['package.json', 'package-lock.json', 'pyproject.toml', 'uv.lock'];
|
|
148
|
+
const hasNode = existsSync(join(root, 'package.json')), hasPython = existsSync(join(root, 'pyproject.toml'));
|
|
149
|
+
await lockDependencies(root);
|
|
150
|
+
const nodePackage = hasNode ? JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) : {};
|
|
151
|
+
const hasNodeDependencies = ['dependencies', 'devDependencies', 'optionalDependencies'].some(key => Object.keys(nodePackage[key] ?? {}).length > 0);
|
|
152
|
+
const runtimes = { ...config.runtimes };
|
|
153
|
+
const executions = Object.values(method.steps).flatMap((s) => [s.do, s.check, ...Object.values(s.effects ?? {}).flatMap((e) => [e.observe, e.judge])]).filter(Boolean);
|
|
154
|
+
for (const tool of Object.values({ ...config.tools, ...method.tools }))
|
|
155
|
+
if (tool.run)
|
|
156
|
+
executions.push(tool.run);
|
|
157
|
+
const needsPython = hasPython || executions.some(e => e?.runtime === 'python' && !runtimes.python);
|
|
158
|
+
const key = createHash('sha256').update(JSON.stringify([process.platform, process.arch, process.version, needsPython, ...files.map(n => existsSync(join(root, n)) ? readFileSync(join(root, n), 'utf8') : null)])).digest('hex');
|
|
159
|
+
const cache = join(methodCache(), 'environments', key), ready = join(cache, 'ready.json');
|
|
160
|
+
mkdirSync(dirname(cache), { recursive: true, mode: 0o700 });
|
|
161
|
+
const lock = `${cache}.lock`;
|
|
162
|
+
let fd;
|
|
163
|
+
const until = Date.now() + 600_000;
|
|
164
|
+
while (fd === undefined) {
|
|
165
|
+
try {
|
|
166
|
+
fd = openSync(lock, 'wx', 0o600);
|
|
167
|
+
writeFileSync(fd, String(process.pid));
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
if (e.code !== 'EEXIST')
|
|
171
|
+
throw e;
|
|
172
|
+
try {
|
|
173
|
+
const pid = Number(readFileSync(lock, 'utf8'));
|
|
174
|
+
if (pid > 0)
|
|
175
|
+
process.kill(pid, 0);
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
if (e.code === 'ESRCH') {
|
|
179
|
+
rmSync(lock, { force: true });
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (Date.now() > until)
|
|
184
|
+
throw Error('Another process is preparing this Method. Wait, then run again.');
|
|
185
|
+
await new Promise(r => setTimeout(r, 300));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
if (!existsSync(ready)) {
|
|
190
|
+
rmSync(cache, { recursive: true, force: true });
|
|
191
|
+
mkdirSync(cache, { recursive: true, mode: 0o700 });
|
|
192
|
+
for (const name of files)
|
|
193
|
+
if (existsSync(join(root, name)))
|
|
194
|
+
cpSync(join(root, name), join(cache, name));
|
|
195
|
+
if (hasNode)
|
|
196
|
+
await npm(cache, ['ci', '--engine-strict', '--no-audit', '--no-fund']);
|
|
197
|
+
if (needsPython) {
|
|
198
|
+
const uvBin = await uv();
|
|
199
|
+
if (hasPython)
|
|
200
|
+
await command(uvBin, ['sync', '--locked', '--no-install-project', '--project', cache], cache);
|
|
201
|
+
else
|
|
202
|
+
await command(uvBin, ['venv', '--python', '3.12', join(cache, '.venv')], cache);
|
|
203
|
+
}
|
|
204
|
+
const browser = join(cache, 'node_modules', 'playwright', 'cli.js');
|
|
205
|
+
if (existsSync(browser))
|
|
206
|
+
await command(process.execPath, [browser, 'install', 'chromium'], cache);
|
|
207
|
+
writeFileSync(`${ready}.tmp`, JSON.stringify({ key, created_at: new Date().toISOString() }), { mode: 0o600 });
|
|
208
|
+
renameSync(`${ready}.tmp`, ready);
|
|
209
|
+
}
|
|
210
|
+
if (hasNodeDependencies && !existsSync(join(cache, 'node_modules', '.package-lock.json'))) {
|
|
211
|
+
rmSync(ready, { force: true });
|
|
212
|
+
throw Object.assign(Error('Rebuild incomplete Node packages.'), { code: 'cache_damaged' });
|
|
213
|
+
}
|
|
214
|
+
const python = join(cache, '.venv', 'bin', 'python');
|
|
215
|
+
if (needsPython) {
|
|
216
|
+
try {
|
|
217
|
+
await command(python, ['-c', 'import sys; print(sys.version)'], cache);
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
rmSync(ready, { force: true });
|
|
221
|
+
throw Object.assign(e, { code: 'cache_damaged' });
|
|
222
|
+
}
|
|
223
|
+
if (!runtimes.python)
|
|
224
|
+
runtimes.python = { command: python, version: (await command(python, ['--version'], cache)).stdout.trim() };
|
|
225
|
+
}
|
|
226
|
+
if (executions.some(e => e?.runtime === 'node') && !runtimes.node)
|
|
227
|
+
runtimes.node = { command: process.execPath, version: process.version };
|
|
228
|
+
const browser = join(cache, 'node_modules', 'playwright', 'index.js');
|
|
229
|
+
if (existsSync(browser)) {
|
|
230
|
+
try {
|
|
231
|
+
await command(process.execPath, ['-e', `const {chromium}=require(${JSON.stringify(browser)}); (async()=>{const b=await chromium.launch({headless:true});try{const p=await b.newPage();await p.goto('data:text/html,<title>Method</title>');if(await p.title()!=='Method')throw Error('Browser page did not load');}finally{await b.close()}})().catch(e=>{console.error(e.message);process.exitCode=1})`], cache);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
throw Error(`The browser cannot start on this computer. ${e.message}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { config: { ...config, runtimes }, processPath: [cliBin(), dirname(process.execPath), ...(needsPython ? [dirname(python)] : []), process.env.PATH ?? ''].join(delimiter),
|
|
238
|
+
prepareBundle: async (bundle) => { const modules = join(cache, 'node_modules'), dest = join(bundle, 'node_modules'); if (existsSync(modules) && !existsSync(dest))
|
|
239
|
+
symlinkSync(modules, dest, 'dir'); } };
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
closeSync(fd);
|
|
243
|
+
rmSync(lock, { force: true });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type ProcessCommand = {
|
|
2
|
+
command: string;
|
|
3
|
+
args?: string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
timeout_ms?: number;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
stdout_path?: string;
|
|
8
|
+
stderr_path?: string;
|
|
9
|
+
pid_path?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Run an explicitly configured local command, without shell interpolation. */
|
|
12
|
+
export declare function runProcess(command: ProcessCommand, input: string): Promise<{
|
|
13
|
+
stdout: string;
|
|
14
|
+
stderr: string;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { openSync, closeSync, writeSync, writeFileSync } from "node:fs";
|
|
3
|
+
/** Run an explicitly configured local command, without shell interpolation. */
|
|
4
|
+
export async function runProcess(command, input) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
let stdoutFile;
|
|
7
|
+
let stderrFile;
|
|
8
|
+
try {
|
|
9
|
+
if (command.stdout_path)
|
|
10
|
+
stdoutFile = openSync(command.stdout_path, "wx", 0o600);
|
|
11
|
+
if (command.stderr_path)
|
|
12
|
+
stderrFile = openSync(command.stderr_path, "wx", 0o600);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (stdoutFile !== undefined)
|
|
16
|
+
closeSync(stdoutFile);
|
|
17
|
+
reject(error);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const child = spawn(command.command, command.args ?? [], {
|
|
21
|
+
cwd: command.cwd, env: command.env ?? process.env,
|
|
22
|
+
stdio: ["pipe", "pipe", "pipe"], detached: process.platform !== "win32"
|
|
23
|
+
});
|
|
24
|
+
let stdout = "";
|
|
25
|
+
let stderr = "";
|
|
26
|
+
let failure = null;
|
|
27
|
+
let killTimer;
|
|
28
|
+
const kill = (signal) => {
|
|
29
|
+
if (!child.pid)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
if (process.platform !== "win32")
|
|
33
|
+
process.kill(-child.pid, signal);
|
|
34
|
+
else
|
|
35
|
+
child.kill(signal);
|
|
36
|
+
}
|
|
37
|
+
catch { /* Already exited. */ }
|
|
38
|
+
};
|
|
39
|
+
const stop = (message) => {
|
|
40
|
+
if (failure)
|
|
41
|
+
return;
|
|
42
|
+
failure = new Error(message);
|
|
43
|
+
kill("SIGTERM");
|
|
44
|
+
killTimer = setTimeout(() => kill("SIGKILL"), 2_000);
|
|
45
|
+
};
|
|
46
|
+
const timer = setTimeout(() => stop("PROCESS_TIMEOUT: inspect effects before retrying."), command.timeout_ms ?? 300_000);
|
|
47
|
+
const interrupt = () => stop("PROCESS_INTERRUPTED: inspect effects before resuming.");
|
|
48
|
+
process.on("SIGINT", interrupt);
|
|
49
|
+
process.on("SIGTERM", interrupt);
|
|
50
|
+
child.on("spawn", () => {
|
|
51
|
+
try {
|
|
52
|
+
if (command.pid_path)
|
|
53
|
+
writeFileSync(command.pid_path, JSON.stringify({ pid: child.pid, parent_pid: process.pid }) + "\n", { mode: 0o600 });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
stop("PROCESS_LOG_FAILED");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
child.stdout.on("data", (chunk) => {
|
|
60
|
+
try {
|
|
61
|
+
if (stdoutFile !== undefined)
|
|
62
|
+
writeSync(stdoutFile, chunk);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
stop("PROCESS_LOG_FAILED");
|
|
66
|
+
}
|
|
67
|
+
stdout += chunk.toString();
|
|
68
|
+
if (stdout.length > 5_000_000)
|
|
69
|
+
stop("PROCESS_OUTPUT_LIMIT");
|
|
70
|
+
});
|
|
71
|
+
child.stderr.on("data", (chunk) => {
|
|
72
|
+
try {
|
|
73
|
+
if (stderrFile !== undefined)
|
|
74
|
+
writeSync(stderrFile, chunk);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
stop("PROCESS_LOG_FAILED");
|
|
78
|
+
}
|
|
79
|
+
// Keep bounded diagnostics in memory; callers decide what can be saved.
|
|
80
|
+
stderr = (stderr + chunk.toString()).slice(-64_000);
|
|
81
|
+
});
|
|
82
|
+
child.on("error", (error) => { clearTimeout(timer); reject(error); });
|
|
83
|
+
child.on("close", (code) => {
|
|
84
|
+
// The parent can exit on SIGTERM while a descendant ignores it. Stop the
|
|
85
|
+
// remaining group before cancelling escalation and reporting failure.
|
|
86
|
+
if (failure || code !== 0)
|
|
87
|
+
kill("SIGKILL");
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
process.off("SIGINT", interrupt);
|
|
90
|
+
process.off("SIGTERM", interrupt);
|
|
91
|
+
if (stdoutFile !== undefined)
|
|
92
|
+
closeSync(stdoutFile);
|
|
93
|
+
if (stderrFile !== undefined)
|
|
94
|
+
closeSync(stderrFile);
|
|
95
|
+
if (killTimer)
|
|
96
|
+
clearTimeout(killTimer);
|
|
97
|
+
if (failure)
|
|
98
|
+
reject(failure);
|
|
99
|
+
else if (code !== 0)
|
|
100
|
+
reject(new Error(`PROCESS_FAILED: ${command.command} exited ${code}. ${stderr.slice(-1500)}`));
|
|
101
|
+
else
|
|
102
|
+
resolve({ stdout, stderr });
|
|
103
|
+
});
|
|
104
|
+
child.stdin.on("error", () => { });
|
|
105
|
+
child.stdin.end(input);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ProductionWorker, type WorkerOptions } from "./worker.js";
|
|
2
|
+
export type RunStatus = "queued" | "running" | "waiting" | "succeeded" | "failed" | "cancelled";
|
|
3
|
+
/** What an ask handler gets. Return the answer, or return nothing and call answer() (or method.runs.answer) later. */
|
|
4
|
+
export type AskEvent = {
|
|
5
|
+
run_id: string;
|
|
6
|
+
/** STEP:ITERATION, for example approve:0. */
|
|
7
|
+
step: string;
|
|
8
|
+
question: string;
|
|
9
|
+
/** JSON Schema of the answer: the ask step's out types. */
|
|
10
|
+
form: Record<string, unknown>;
|
|
11
|
+
answer: (value: Record<string, unknown>) => Promise<ProductionRun>;
|
|
12
|
+
};
|
|
13
|
+
export type AskHandler = (event: AskEvent) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;
|
|
14
|
+
export type StartRunOptions = {
|
|
15
|
+
/** The Method ID (wf_…). */
|
|
16
|
+
method: string;
|
|
17
|
+
/** "published" (the default) or a version ID. A run stays on one version. */
|
|
18
|
+
version?: string;
|
|
19
|
+
inputs?: Record<string, unknown>;
|
|
20
|
+
/** The same key returns the same run. Use one key for each business event, for example a request ID. */
|
|
21
|
+
idempotencyKey?: string;
|
|
22
|
+
/** HTTPS URL that receives {run_id, status, …} signed with Method-Signature: a needs_input event and the end of the run. */
|
|
23
|
+
webhookUrl?: string;
|
|
24
|
+
/** This app answers the run's questions. Without a handler or a webhook, questions go to the team inbox and email. */
|
|
25
|
+
onAsk?: AskHandler;
|
|
26
|
+
};
|
|
27
|
+
export type StartedRun = {
|
|
28
|
+
run_id: string;
|
|
29
|
+
version_id: string;
|
|
30
|
+
status: RunStatus;
|
|
31
|
+
};
|
|
32
|
+
export type ProductionQuestion = {
|
|
33
|
+
id: string;
|
|
34
|
+
run_id: string;
|
|
35
|
+
step: string;
|
|
36
|
+
answered_by: "app" | "team";
|
|
37
|
+
asked_at: string;
|
|
38
|
+
expires_at: string | null;
|
|
39
|
+
question?: string;
|
|
40
|
+
form?: Record<string, unknown>;
|
|
41
|
+
sealed?: string;
|
|
42
|
+
};
|
|
43
|
+
export type ProductionRun = {
|
|
44
|
+
run_id: string;
|
|
45
|
+
method_id: string;
|
|
46
|
+
version_id: string;
|
|
47
|
+
status: RunStatus;
|
|
48
|
+
run_data: "account" | "device";
|
|
49
|
+
attempts: number;
|
|
50
|
+
created_at: string;
|
|
51
|
+
started_at: string | null;
|
|
52
|
+
finished_at: string | null;
|
|
53
|
+
cancel_requested: boolean;
|
|
54
|
+
error: {
|
|
55
|
+
code?: string;
|
|
56
|
+
message: string;
|
|
57
|
+
} | null;
|
|
58
|
+
record_run_id: string | null;
|
|
59
|
+
steps: {
|
|
60
|
+
id: string;
|
|
61
|
+
step_id: string;
|
|
62
|
+
status: string;
|
|
63
|
+
started_at: string | null;
|
|
64
|
+
finished_at: string | null;
|
|
65
|
+
}[];
|
|
66
|
+
/** Present while the run waits for an answer. A device run's question arrives sealed; the library opens it. */
|
|
67
|
+
question?: ProductionQuestion;
|
|
68
|
+
/** For run_data "account", from the server. For a device run, only when this process's worker ran it. */
|
|
69
|
+
result?: unknown;
|
|
70
|
+
};
|
|
71
|
+
export type MethodOptions = {
|
|
72
|
+
apiKey?: string;
|
|
73
|
+
server?: string;
|
|
74
|
+
fetch?: typeof fetch;
|
|
75
|
+
/** The default ask handler of runs that this process starts. */
|
|
76
|
+
onAsk?: AskHandler;
|
|
77
|
+
/** false: method.run() does not start a worker in this process (use method worker or worker.start()). */
|
|
78
|
+
worker?: boolean | Omit<WorkerOptions, "methods">;
|
|
79
|
+
};
|
|
80
|
+
export declare class MethodApiError extends Error {
|
|
81
|
+
readonly status: number;
|
|
82
|
+
readonly code: string;
|
|
83
|
+
readonly body?: unknown | undefined;
|
|
84
|
+
constructor(status: number, code: string, message: string, body?: unknown | undefined);
|
|
85
|
+
}
|
|
86
|
+
/** The production API client. Every request sends Authorization: Bearer CREDENTIAL. */
|
|
87
|
+
export declare class ServiceApi {
|
|
88
|
+
readonly apiKey: string;
|
|
89
|
+
readonly fetcher: typeof fetch;
|
|
90
|
+
readonly server: string;
|
|
91
|
+
constructor(apiKey: string, server: string, fetcher?: typeof fetch);
|
|
92
|
+
request<T>(path: string, method?: string, body?: unknown, timeoutMs?: number, signal?: AbortSignal): Promise<{
|
|
93
|
+
status: number;
|
|
94
|
+
body: T;
|
|
95
|
+
}>;
|
|
96
|
+
/** Send or get raw bytes (a run folder). */
|
|
97
|
+
bytes(path: string, method: string, body?: Uint8Array, headers?: Record<string, string>): Promise<Buffer>;
|
|
98
|
+
private send;
|
|
99
|
+
}
|
|
100
|
+
/** Verify a webhook: Method-Signature is t=TIMESTAMP,v1=HEX(HMAC-SHA256(secret, `${t}.${body}`)). Pass the raw body. */
|
|
101
|
+
export declare function verifyWebhook(body: string | Uint8Array, signature: string | null | undefined, secret: string, options?: {
|
|
102
|
+
toleranceSeconds?: number;
|
|
103
|
+
now?: number;
|
|
104
|
+
}): boolean;
|
|
105
|
+
/** METHOD_API_KEY and METHOD_SERVER from .env in a folder. Other lines are not read. */
|
|
106
|
+
export declare function dotenvValues(folder?: string): {
|
|
107
|
+
METHOD_API_KEY?: string;
|
|
108
|
+
METHOD_SERVER?: string;
|
|
109
|
+
};
|
|
110
|
+
/** The credential of the libraries: apiKey, METHOD_API_KEY, .env, then the CLI sign-in. */
|
|
111
|
+
export declare function resolveCredential(options?: {
|
|
112
|
+
apiKey?: string;
|
|
113
|
+
server?: string;
|
|
114
|
+
}): {
|
|
115
|
+
key: string;
|
|
116
|
+
server: string;
|
|
117
|
+
};
|
|
118
|
+
export declare class Method {
|
|
119
|
+
private readonly options;
|
|
120
|
+
readonly api: ServiceApi;
|
|
121
|
+
/** Results of runs that this process's worker finished. A device run's result exists only here. */
|
|
122
|
+
private readonly local;
|
|
123
|
+
private readonly waiting;
|
|
124
|
+
private readonly handlers;
|
|
125
|
+
private readonly asked;
|
|
126
|
+
private readonly runData;
|
|
127
|
+
private readonly lazyMethods;
|
|
128
|
+
private lazyWorker;
|
|
129
|
+
private lazyRuns;
|
|
130
|
+
readonly runs: {
|
|
131
|
+
start: (options: StartRunOptions) => Promise<StartedRun>;
|
|
132
|
+
get: (runId: string) => Promise<ProductionRun>;
|
|
133
|
+
cancel: (runId: string) => Promise<ProductionRun>;
|
|
134
|
+
/** Answer the waiting question of a run. The answer must match the question's form. */
|
|
135
|
+
answer: (runId: string, answer: Record<string, unknown>) => Promise<ProductionRun>;
|
|
136
|
+
/**
|
|
137
|
+
* Wait until the run ends. Checks the server every intervalMs, and returns at once when this process's worker finishes
|
|
138
|
+
* it. A question of a run with an ask handler goes to the handler.
|
|
139
|
+
*/
|
|
140
|
+
wait: (runId: string, options?: {
|
|
141
|
+
timeoutMs?: number;
|
|
142
|
+
intervalMs?: number;
|
|
143
|
+
onAsk?: AskHandler;
|
|
144
|
+
}) => Promise<ProductionRun>;
|
|
145
|
+
/** Start a run and wait for its result. Needs a worker: method.run() starts one in this process. */
|
|
146
|
+
run: (options: StartRunOptions & {
|
|
147
|
+
timeoutMs?: number;
|
|
148
|
+
intervalMs?: number;
|
|
149
|
+
}) => Promise<ProductionRun>;
|
|
150
|
+
};
|
|
151
|
+
readonly worker: {
|
|
152
|
+
/** Claim and run queued runs in this process. Results reach onResult, runs.wait, and the dashboard. */
|
|
153
|
+
start: (options?: WorkerOptions) => ProductionWorker;
|
|
154
|
+
};
|
|
155
|
+
readonly webhooks: {
|
|
156
|
+
verify: typeof verifyWebhook;
|
|
157
|
+
};
|
|
158
|
+
static readonly webhooks: {
|
|
159
|
+
verify: typeof verifyWebhook;
|
|
160
|
+
};
|
|
161
|
+
constructor(options?: MethodOptions);
|
|
162
|
+
/**
|
|
163
|
+
* Run a published Method and wait for its result. The first call starts a worker in this process (unless the options
|
|
164
|
+
* say worker: false), so the run needs nothing else running. The worker stops when no method.run() call is waiting,
|
|
165
|
+
* so a script that calls method.run() exits when it is done; close() is not needed.
|
|
166
|
+
*/
|
|
167
|
+
run(options: StartRunOptions & {
|
|
168
|
+
timeoutMs?: number;
|
|
169
|
+
intervalMs?: number;
|
|
170
|
+
}): Promise<ProductionRun>;
|
|
171
|
+
/** Stop this process's worker. Runs in progress finish first. */
|
|
172
|
+
close(options?: {
|
|
173
|
+
cancel?: boolean;
|
|
174
|
+
}): Promise<void>;
|
|
175
|
+
private ensureWorker;
|
|
176
|
+
private wake;
|
|
177
|
+
private keepsOnDevice;
|
|
178
|
+
private openQuestion;
|
|
179
|
+
/** Give a waiting question to the run's ask handler, once. */
|
|
180
|
+
private ask;
|
|
181
|
+
private withLocal;
|
|
182
|
+
}
|