humanish 0.137.0 → 0.139.0
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/CHANGELOG.md +22 -0
- package/dist/cli/commands/runs.js +2 -2
- package/dist/cli/doctor.js +24 -0
- package/dist/keys/key-resolution.js +53 -2
- package/dist/routes/computer-use/bundle-parts.d.ts +3 -0
- package/dist/routes/computer-use/bundle-parts.js +9 -1
- package/dist/routes/computer-use/bundle.js +3 -4
- package/dist/routes/computer-use/e2b-desktop/prepare.js +8 -4
- package/dist/routes/computer-use/fanout-bundle.js +3 -2
- package/dist/routes/computer-use/participant-deps.d.ts +1 -1
- package/dist/routes/computer-use/plan.js +66 -19
- package/dist/routes/computer-use/setup.js +14 -7
- package/dist/routes/computer-use/types.d.ts +3 -0
- package/dist/routes/terminal/live-sandbox.d.ts +2 -0
- package/dist/routes/terminal/live-sandbox.js +8 -3
- package/dist/routes/terminal/plan.js +49 -6
- package/dist/routes/terminal/session.js +8 -1
- package/dist/run/bundle-shape.js +1 -1
- package/dist/run/bundle.d.ts +5 -3
- package/dist/run/cost-summary.js +9 -4
- package/dist/run/feedback-shape.js +1 -0
- package/dist/run/pricing.d.ts +11 -0
- package/dist/run/pricing.js +18 -5
- package/dist/run/reclaim-outcomes.d.ts +4 -1
- package/dist/run/reclaim-outcomes.js +8 -6
- package/dist/run/reclaim.d.ts +4 -1
- package/dist/run/reclaim.js +81 -34
- package/dist/run/sandbox-creates.d.ts +7 -4
- package/dist/run/sandbox-creates.js +17 -9
- package/dist/run/sandbox-receipts.d.ts +13 -5
- package/dist/run/sandbox-receipts.js +18 -6
- package/dist/study/composition-rules.js +14 -12
- package/dist/study/doctor.js +1 -1
- package/dist/study/parse/execution.js +6 -2
- package/dist/study/plan-base.d.ts +1 -0
- package/dist/study/plan-base.js +2 -0
- package/dist/study/plan-types.d.ts +6 -1
- package/dist/study/requirements.js +7 -2
- package/dist/study/routing.d.ts +1 -1
- package/dist/study/routing.js +1 -1
- package/dist/study/types.d.ts +1 -1
- package/dist/study/warnings.js +3 -1
- package/dist/substrates/e2b/desktop-resources.d.ts +4 -2
- package/dist/substrates/e2b/desktop-resources.js +2 -2
- package/dist/substrates/e2b/peer-install.js +1 -1
- package/dist/substrates/e2b/sandbox.d.ts +3 -0
- package/dist/substrates/e2b/sandbox.js +5 -4
- package/dist/substrates/e2b/sdk.d.ts +5 -0
- package/dist/substrates/e2b/sdk.js +12 -0
- package/dist/substrates/modal/commands.d.ts +16 -0
- package/dist/substrates/modal/commands.js +174 -0
- package/dist/substrates/modal/desktop.d.ts +16 -0
- package/dist/substrates/modal/desktop.js +195 -0
- package/dist/substrates/modal/image.d.ts +11 -0
- package/dist/substrates/modal/image.js +71 -0
- package/dist/substrates/modal/sandbox.d.ts +30 -0
- package/dist/substrates/modal/sandbox.js +325 -0
- package/dist/substrates/modal/sdk.d.ts +112 -0
- package/dist/substrates/modal/sdk.js +61 -0
- package/docs/architecture/terminal-product-route.md +1 -1
- package/docs/contracts/run-bundle.md +5 -2
- package/docs/contracts/schemas.md +8 -7
- package/package.json +8 -2
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** Which Modal workspace and environment a call reaches. */
|
|
2
|
+
export interface ModalConnection {
|
|
3
|
+
tokenId: string;
|
|
4
|
+
tokenSecret: string;
|
|
5
|
+
/** Modal's own default environment is used when unset. */
|
|
6
|
+
environment?: string;
|
|
7
|
+
}
|
|
8
|
+
/** The variables a Modal connection is read from, as Modal's own CLI and SDK name them. */
|
|
9
|
+
export declare const MODAL_TOKEN_ID_ENV = "MODAL_TOKEN_ID";
|
|
10
|
+
export declare const MODAL_TOKEN_SECRET_ENV = "MODAL_TOKEN_SECRET";
|
|
11
|
+
/**
|
|
12
|
+
* The connection an env names, or undefined when either token is missing. The SDK would otherwise
|
|
13
|
+
* fall back to process.env and ~/.modal.toml, which a library caller's env may not match, and a
|
|
14
|
+
* release could then reach another workspace than the create.
|
|
15
|
+
*/
|
|
16
|
+
export declare function modalConnection(env: Readonly<Record<string, string | undefined>>): ModalConnection | undefined;
|
|
17
|
+
export interface ModalReadStream extends AsyncIterable<string | Uint8Array> {
|
|
18
|
+
readText(): Promise<string>;
|
|
19
|
+
readBytes(): Promise<Uint8Array>;
|
|
20
|
+
}
|
|
21
|
+
export interface ModalProcess {
|
|
22
|
+
stdout: ModalReadStream;
|
|
23
|
+
stderr: ModalReadStream;
|
|
24
|
+
stdin: {
|
|
25
|
+
writeText(text: string): Promise<void>;
|
|
26
|
+
writeBytes(bytes: Uint8Array): Promise<void>;
|
|
27
|
+
/** End of input after what was written. */
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
/** End of input at offset 0: it ends input only before anything was written. */
|
|
31
|
+
closeStdin(): Promise<void>;
|
|
32
|
+
wait(): Promise<number>;
|
|
33
|
+
}
|
|
34
|
+
export interface ModalExecParams {
|
|
35
|
+
mode?: "text" | "binary";
|
|
36
|
+
stdout?: "pipe" | "ignore";
|
|
37
|
+
stderr?: "pipe" | "ignore";
|
|
38
|
+
workdir?: string;
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
env?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
export interface ModalSandbox {
|
|
43
|
+
readonly sandboxId: string;
|
|
44
|
+
exec(command: string[], params?: ModalExecParams): Promise<ModalProcess>;
|
|
45
|
+
terminate(): Promise<void>;
|
|
46
|
+
/** The exit code once the sandbox has finished, null while it runs. */
|
|
47
|
+
poll(): Promise<number | null>;
|
|
48
|
+
/** Resolves with the exit code once the sandbox has finished. */
|
|
49
|
+
wait(): Promise<number>;
|
|
50
|
+
getTags(): Promise<Record<string, string>>;
|
|
51
|
+
tunnels(timeoutMs?: number): Promise<Record<number, {
|
|
52
|
+
url: string;
|
|
53
|
+
}>>;
|
|
54
|
+
}
|
|
55
|
+
export interface ModalSandboxCreateParams {
|
|
56
|
+
cpu?: number;
|
|
57
|
+
memoryMiB?: number;
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
workdir?: string;
|
|
60
|
+
env?: Record<string, string>;
|
|
61
|
+
encryptedPorts?: number[];
|
|
62
|
+
blockNetwork?: boolean;
|
|
63
|
+
outboundCidrAllowlist?: string[];
|
|
64
|
+
outboundDomainAllowlist?: string[];
|
|
65
|
+
runtime?: "gvisor" | "vm";
|
|
66
|
+
tags?: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
export interface ModalImage {
|
|
69
|
+
dockerfileCommands(commands: string[]): ModalImage;
|
|
70
|
+
}
|
|
71
|
+
export interface ModalClient {
|
|
72
|
+
apps: {
|
|
73
|
+
fromName(name: string, params?: {
|
|
74
|
+
environment?: string;
|
|
75
|
+
createIfMissing?: boolean;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
appId: string;
|
|
78
|
+
}>;
|
|
79
|
+
};
|
|
80
|
+
images: {
|
|
81
|
+
fromRegistry(tag: string): ModalImage;
|
|
82
|
+
};
|
|
83
|
+
sandboxes: {
|
|
84
|
+
create(app: {
|
|
85
|
+
appId: string;
|
|
86
|
+
}, image: ModalImage, params?: ModalSandboxCreateParams): Promise<ModalSandbox>;
|
|
87
|
+
fromId(sandboxId: string): Promise<ModalSandbox>;
|
|
88
|
+
list(params?: {
|
|
89
|
+
appId?: string;
|
|
90
|
+
tags?: Record<string, string>;
|
|
91
|
+
environment?: string;
|
|
92
|
+
}): AsyncIterable<ModalSandbox>;
|
|
93
|
+
};
|
|
94
|
+
close?(): void;
|
|
95
|
+
}
|
|
96
|
+
export interface ModalModule {
|
|
97
|
+
ModalClient: new (params: ModalConnection) => ModalClient;
|
|
98
|
+
}
|
|
99
|
+
export declare function loadModalModule(): Promise<ModalModule>;
|
|
100
|
+
/**
|
|
101
|
+
* `error` with the connection's tokens replaced in its message and stack. The SDK can quote a
|
|
102
|
+
* token, for instance when gRPC refuses one with a stray zero-width character as metadata, and a
|
|
103
|
+
* provider error reaches run warnings and reclaim receipts.
|
|
104
|
+
*/
|
|
105
|
+
export declare function withoutTokens(error: unknown, connection: ModalConnection): unknown;
|
|
106
|
+
/**
|
|
107
|
+
* Whether `error` is Modal's answer that no sandbox has this id: a gRPC NOT_FOUND (code 5), which
|
|
108
|
+
* the SDK raises from the first call on a handle `fromId` returned for an unknown id.
|
|
109
|
+
*/
|
|
110
|
+
export declare function isModalNotFoundError(error: unknown): boolean;
|
|
111
|
+
/** Modal ends a sandbox after 24 hours on every plan. */
|
|
112
|
+
export declare const MODAL_MAX_SANDBOX_MS: number;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The optional `modal` peer: a lazy loader and the structural subset of its JavaScript SDK that
|
|
2
|
+
// humanish calls. Like @e2b/desktop it stays out of the published tarball, and these interfaces
|
|
3
|
+
// keep its types out of humanish's own declarations.
|
|
4
|
+
import { desktopPeerAdvice, humanishInstall } from "../e2b/peer-install.js";
|
|
5
|
+
/** The variables a Modal connection is read from, as Modal's own CLI and SDK name them. */
|
|
6
|
+
export const MODAL_TOKEN_ID_ENV = "MODAL_TOKEN_ID";
|
|
7
|
+
export const MODAL_TOKEN_SECRET_ENV = "MODAL_TOKEN_SECRET";
|
|
8
|
+
const MODAL_ENVIRONMENT_ENV = "MODAL_ENVIRONMENT";
|
|
9
|
+
/**
|
|
10
|
+
* The connection an env names, or undefined when either token is missing. The SDK would otherwise
|
|
11
|
+
* fall back to process.env and ~/.modal.toml, which a library caller's env may not match, and a
|
|
12
|
+
* release could then reach another workspace than the create.
|
|
13
|
+
*/
|
|
14
|
+
export function modalConnection(env) {
|
|
15
|
+
const tokenId = env[MODAL_TOKEN_ID_ENV]?.trim();
|
|
16
|
+
const tokenSecret = env[MODAL_TOKEN_SECRET_ENV]?.trim();
|
|
17
|
+
if (!tokenId || !tokenSecret)
|
|
18
|
+
return undefined;
|
|
19
|
+
const environment = env[MODAL_ENVIRONMENT_ENV]?.trim();
|
|
20
|
+
return { tokenId, tokenSecret, ...(environment ? { environment } : {}) };
|
|
21
|
+
}
|
|
22
|
+
export async function loadModalModule() {
|
|
23
|
+
try {
|
|
24
|
+
return (await import("modal"));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const value = error;
|
|
28
|
+
if (value.code === "ERR_MODULE_NOT_FOUND" && value.message?.includes("'modal'") === true) {
|
|
29
|
+
const { where, advice } = desktopPeerAdvice(humanishInstall(), "modal");
|
|
30
|
+
throw new Error(`Running on Modal requires the optional peer modal${where}. ${advice}`);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `error` with the connection's tokens replaced in its message and stack. The SDK can quote a
|
|
37
|
+
* token, for instance when gRPC refuses one with a stray zero-width character as metadata, and a
|
|
38
|
+
* provider error reaches run warnings and reclaim receipts.
|
|
39
|
+
*/
|
|
40
|
+
export function withoutTokens(error, connection) {
|
|
41
|
+
if (!(error instanceof Error))
|
|
42
|
+
return error;
|
|
43
|
+
const tokens = [connection.tokenId, connection.tokenSecret];
|
|
44
|
+
const hide = (text) => tokens.reduce((hidden, token) => hidden.replaceAll(token, "[REDACTED_MODAL_TOKEN]"), text);
|
|
45
|
+
error.message = hide(error.message);
|
|
46
|
+
if (error.stack !== undefined)
|
|
47
|
+
error.stack = hide(error.stack);
|
|
48
|
+
return error;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether `error` is Modal's answer that no sandbox has this id: a gRPC NOT_FOUND (code 5), which
|
|
52
|
+
* the SDK raises from the first call on a handle `fromId` returned for an unknown id.
|
|
53
|
+
*/
|
|
54
|
+
export function isModalNotFoundError(error) {
|
|
55
|
+
if (error === null || typeof error !== "object")
|
|
56
|
+
return false;
|
|
57
|
+
const value = error;
|
|
58
|
+
return value.name === "NotFoundError" || value.code === 5;
|
|
59
|
+
}
|
|
60
|
+
/** Modal ends a sandbox after 24 hours on every plan. */
|
|
61
|
+
export const MODAL_MAX_SANDBOX_MS = 24 * 60 * 60_000;
|
|
@@ -28,7 +28,7 @@ fail-closed cross-validation, and forward-declared warnings.
|
|
|
28
28
|
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
29
|
| `subject.source` | `terminal-product` |
|
|
30
30
|
| `subject.product` | `{ name, publicSurfaces[], install?, workdir?, upload? }`: the only world the agent sees |
|
|
31
|
-
| `execution.target` | `e2b-terminal` (or absent → implied)
|
|
31
|
+
| `execution.target` | `e2b-terminal` (or absent → implied), or `modal-terminal` for a Modal sandbox |
|
|
32
32
|
| `execution.terminal` | `{ transport: exec-stream, stdin: disabled }` |
|
|
33
33
|
| `execution.runtimeAuth` | `openai-egress` (default) or opt-in `openai-env`; names-only durable evidence |
|
|
34
34
|
| `execution.runtime.version` | Optional exact `@openai/codex` version; observed before keyed execution |
|
|
@@ -703,8 +703,11 @@ Dry runs omit `cost`. A live run that spends nothing records an explicit zero: n
|
|
|
703
703
|
`breakdown` lines, `estimatedTotalUsd: 0`, `ratesAsOf: null` and `fullyEstimated: true`.
|
|
704
704
|
A local scripted run (no model request, no hosted desktop) is such a run; a scripted run on
|
|
705
705
|
a provisioned clone records the clone's desktop as a `laneId: subject` line. A live bundle
|
|
706
|
-
without `cost` has an unmeasured spend. A live terminal run's `cost` prices
|
|
707
|
-
|
|
706
|
+
without `cost` has an unmeasured spend. A live terminal run's `cost` prices its shell sandbox
|
|
707
|
+
as a `desktop-minutes` line from its acquired-to-cleanup span and size: E2B's report
|
|
708
|
+
(`resourceSource: e2b.getInfo`), or the size humanish asked Modal for (`modal.create`), priced at
|
|
709
|
+
Modal's sandbox rate. A computer-use run on `execution.target: modal-desktop` records each
|
|
710
|
+
desktop's `providerResources[].provider` as `modal-desktop` and prices it the same way.
|
|
708
711
|
Its Codex `model-tokens` line prices the agent's tokens from the model passed as `--model`
|
|
709
712
|
(`actor.model`, else `gpt-5.6-sol`). Codex reports usage per turn, summed over the turn's
|
|
710
713
|
requests, so the line prices every token at the base tier and carries
|
|
@@ -336,10 +336,10 @@ The rest of this section names keys by their v3 spelling:
|
|
|
336
336
|
- `subject.appUrl`: a loopback http(s) URL a computer-use actor drives, or (on the
|
|
337
337
|
external-public shared-world route) the non-loopback public deployment used as the
|
|
338
338
|
shared plane (with `publicTarget` + `allowPublicTargets`);
|
|
339
|
-
- `execution`: where it runs (`local`, `e2b-desktop`,
|
|
340
|
-
desktop device/resolution and timeouts. app-url subjects pair
|
|
341
|
-
or `local` with a computer-use actor, or `local` (or absent) with a
|
|
342
|
-
scripted-browser actor; terminal-product subjects pair `e2b-terminal` (or absent →
|
|
339
|
+
- `execution`: where it runs (`local`, `e2b-desktop`, `e2b-terminal`, `modal-desktop` or
|
|
340
|
+
`modal-terminal`), plus desktop device/resolution and timeouts. app-url subjects pair
|
|
341
|
+
`e2b-desktop`, `modal-desktop` or `local` with a computer-use actor, or `local` (or absent) with a
|
|
342
|
+
scripted-browser actor; terminal-product subjects pair `e2b-terminal` or `modal-terminal` (or absent → `e2b-terminal`)
|
|
343
343
|
with a registered terminal actor. clone and local-tree subjects require an
|
|
344
344
|
explicit `e2b-desktop`: any other target, or none, fails to parse, and the
|
|
345
345
|
computer-use and scripted-browser routes refuse it when a library caller skips
|
|
@@ -1297,7 +1297,7 @@ items: []
|
|
|
1297
1297
|
Reserved: `humanish.substrate.v1` is named here for layering intent but has
|
|
1298
1298
|
never shipped; no code emits or validates it. Substrate truth today lives
|
|
1299
1299
|
inside run bundles (per-stream transport and status) and study execution config
|
|
1300
|
-
(`execution.target: local | e2b-desktop | e2b-terminal`). Do not emit this schema.
|
|
1300
|
+
(`execution.target: local | e2b-desktop | e2b-terminal | modal-desktop | modal-terminal`). Do not emit this schema.
|
|
1301
1301
|
|
|
1302
1302
|
## Serve Result And Reserved Control-Plane Namespace
|
|
1303
1303
|
|
|
@@ -1578,12 +1578,13 @@ Three new `.v1` schema tags ship, all additive and optional so
|
|
|
1578
1578
|
lines remain readable.
|
|
1579
1579
|
A desktop line's optional `desktop` object records `minutes`,
|
|
1580
1580
|
`durationBasis: host-acquired-to-cleanup`, observed `resources` (`cpuCount`,
|
|
1581
|
-
`memoryMiB`), `resourceSource
|
|
1581
|
+
`memoryMiB`), `resourceSource` (`e2b.getInfo`, or `modal.create` for the size humanish asked
|
|
1582
|
+
Modal for), and `usdPerSecond` when priceable.
|
|
1582
1583
|
Failed metadata reads record `resourceUnavailableReason`; no resource guess is used.
|
|
1583
1584
|
Resource metadata and missing/unsupported rates produce a null line. A kept or
|
|
1584
1585
|
unconfirmed allocation adds `desktop_lifetime_incomplete` as a second null line.
|
|
1585
1586
|
Live terminal-product runs emit the same summary: one `desktop-minutes` line for
|
|
1586
|
-
the
|
|
1587
|
+
the shell sandbox (its acquired-to-cleanup span and its `e2b.getInfo` or `modal.create` size) and
|
|
1587
1588
|
one `model-tokens` line for the Codex participant, priced from the model the route passed
|
|
1588
1589
|
to Codex (`gpt-5.6-sol` unless the study declares one) with `basis:
|
|
1589
1590
|
aggregated_turns_base_rate`, or `null` with `no_token_usage`. Older terminal bundles record
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "humanish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.139.0",
|
|
4
4
|
"description": "Synthetic user research for apps, CLIs, and agent-facing product flows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
"parse5": "^8.0.1",
|
|
110
110
|
"playwright-core": "^1.63.0",
|
|
111
111
|
"pngjs": "^7.0.0",
|
|
112
|
+
"smol-toml": "^1.9.0",
|
|
112
113
|
"undici": "^8.11.2",
|
|
113
114
|
"yaml": "^2.9.1",
|
|
114
115
|
"zod": "^4.6.5"
|
|
@@ -120,6 +121,7 @@
|
|
|
120
121
|
"axe-core": "4.14.0",
|
|
121
122
|
"fast-check": "catalog:",
|
|
122
123
|
"knip": "6.41.0",
|
|
124
|
+
"modal": "^0.11.1",
|
|
123
125
|
"oxc-parser": "catalog:",
|
|
124
126
|
"oxfmt": "0.73.0",
|
|
125
127
|
"oxlint": "1.88.0",
|
|
@@ -130,11 +132,15 @@
|
|
|
130
132
|
"vitest": "catalog:"
|
|
131
133
|
},
|
|
132
134
|
"peerDependencies": {
|
|
133
|
-
"@e2b/desktop": "^2.3.2"
|
|
135
|
+
"@e2b/desktop": "^2.3.2",
|
|
136
|
+
"modal": "^0.11.1"
|
|
134
137
|
},
|
|
135
138
|
"peerDependenciesMeta": {
|
|
136
139
|
"@e2b/desktop": {
|
|
137
140
|
"optional": true
|
|
141
|
+
},
|
|
142
|
+
"modal": {
|
|
143
|
+
"optional": true
|
|
138
144
|
}
|
|
139
145
|
},
|
|
140
146
|
"engines": {
|