@pome-sh/cli 0.43.0 → 0.43.1

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/README.md CHANGED
@@ -1,168 +1,289 @@
1
1
  # Pome CLI
2
2
 
3
- The `pome` command runs AI-agent tasks against resettable digital twins of
4
- real SaaS APIs (GitHub, Stripe, …), captures the trace, and gets a verdict from
5
- Pome cloud.
3
+ The `pome` CLI runs AI-agent tasks against resettable digital twins of real SaaS APIs.
4
+ It records each run and sends hosted runs to Pome for evaluation.
6
5
 
7
- The CLI is **capture-only**: it records raw traces and never scores, judges, or
8
- correlates locally. A verdict comes only from the cloud — a hosted `pome run`
9
- prints it to the terminal and records it to the dashboard, and
10
- `pome eval <run-dir>` uploads a captured trace for a cloud verdict.
6
+ The CLI does not score locally.
11
7
 
12
- **📚 Full documentation lives at [docs.pome.sh](https://docs.pome.sh).**
13
- Run `pome --help` (or `pome help <command>`) for the CLI reference, and
14
- `pome docs getting-started` to print the quickstart's URL.
8
+ - `pome run` uses the hosted workflow by default. It records the run and prints the hosted verdict.
9
+ - `pome run --local` uses local twins. It records one trace and does not request a verdict.
10
+ - `pome eval` uploads existing local artifacts and prints the hosted verdict.
11
+
12
+ See [docs.pome.sh](https://docs.pome.sh) for the full documentation.
13
+ Use `pome --help` or `pome help <command>` for command details.
14
+
15
+ ## Requirements
16
+
17
+ - Node.js 24 or later
18
+ - A Pome account and API key for hosted runs and evaluations
19
+ - A supported model-provider credential when the agent calls a model provider
20
+
21
+ Local twin commands do not require a Pome account.
15
22
 
16
23
  ## Install
17
24
 
25
+ Install the CLI globally:
26
+
18
27
  ```bash
19
28
  npm install -g @pome-sh/cli
20
- pome --help
21
29
  ```
22
30
 
23
- Or run it without installing: `npx @pome-sh/cli <command>` e.g.
24
- `npx @pome-sh/cli twin start github` boots a local GitHub twin with nothing
25
- but Node ≥ 24.
31
+ You can also run one command without a global installation:
32
+
33
+ ```bash
34
+ npx @pome-sh/cli twin start github
35
+ ```
36
+
37
+ ## Set Up A Project
26
38
 
27
- Gmail is first-party too:
39
+ Create the starter project in an empty directory:
28
40
 
29
41
  ```bash
30
- npx @pome-sh/cli twin start gmail --port 3336
31
- # prints POME_GMAIL_REST_URL, POME_GMAIL_MCP_URL, and POME_GMAIL_TOKEN
32
- pome tasks gmail --copy
42
+ mkdir pome-example
43
+ cd pome-example
44
+ pome init
45
+ pome login
46
+ pome register agent my-agent
47
+ pome run tasks/01-bug-happy-path.md
33
48
  ```
34
49
 
35
- `POME_GMAIL_TOKEN` is the same Pome session JWT as `POME_AUTH_TOKEN`; it is not
36
- a Google OAuth token. Hosted Gmail availability is gated separately from the
37
- local/OSS package release.
50
+ `pome init` writes `pome.json`, starter tasks, and an example agent in an empty directory.
51
+ In an existing project, it writes only the manifest unless you use `--starter`.
52
+
53
+ Set `command` in `pome.json`, or pass `--agent <command>` to `pome run`.
54
+
55
+ ## Hosted Workflow
56
+
57
+ Hosted execution is the default for `pome run`.
58
+ The command creates hosted sandboxes, runs the agent, uploads the artifacts, and prints the verdict.
38
59
 
39
- ## Quickstart
60
+ Use `POME_API_KEY` instead of `pome login` in CI.
61
+ Use `-n <count>` to run a hosted trial group of 1 to 20 trials.
62
+ The task `runs` field supplies the count when you omit `-n`.
63
+
64
+ ## Local Capture And Hosted Evaluation
65
+
66
+ Use `--local` to run one task against in-process twins.
67
+ This command records artifacts but never scores them.
40
68
 
41
69
  ```bash
42
- pome login # one-time; opens the dashboard to sign in
43
- pome init # scaffolds tasks/, examples/agents/, runs/, pome.json
44
- pome register agent my-agent # scopes runs to this project
45
- pome run tasks/01-bug-happy-path.md --agent "node examples/agents/scripted-triage-agent.ts"
46
- pome inspect latest # trace/audit view of the last run
70
+ pome run --local tasks/01-bug-happy-path.md
71
+ pome inspect latest
72
+ pome eval
47
73
  ```
48
74
 
49
- To capture a trace without the cloud (self-host), then get a verdict later:
75
+ `pome eval` uses `<artifacts-dir>/latest.json` when you omit the run directory.
76
+ You can also give the directory explicitly:
50
77
 
51
78
  ```bash
52
- pome run --local tasks/01-bug-happy-path.md # captures a raw trace only, no verdict
53
- pome eval runs/01-bug-happy-path/<run-id> # uploads it for a cloud verdict
79
+ pome eval runs/01-bug-happy-path/<run-id>
54
80
  ```
55
81
 
56
- ## Start from an example
82
+ `pome eval` uploads the trace to Pome and prints the hosted verdict.
83
+ It does not add a local score.
84
+
85
+ Do not combine `--local` with `-n`.
86
+ Local capture always runs one trial.
87
+
88
+ ## Standalone Twins
57
89
 
58
- `pome init --example <id>` fetches a complete, runnable example — its agent,
59
- its tasks, its `pome.json` and its lockfile — into `./<id>`:
90
+ Start a long-running local twin:
60
91
 
61
92
  ```bash
62
- pome init --example minimal-viktor # a merge bot on the GitHub + Slack twins
63
- cd minimal-viktor && npm install
64
- pome run tasks/01-clean-merge.md
93
+ pome twin start gmail --port 3336
65
94
  ```
66
95
 
67
- An example is named by **id**, never by path that is the whole point. The ids
68
- are derived from the example directories rather than restated anywhere, so an
69
- example that is renamed or deleted stops answering to the old id on the next
70
- command, with the valid ids printed underneath, instead of going quietly 404 in
71
- a link someone typed months ago. An unknown id lists every available one:
96
+ The command prints the REST URL, MCP URL, and bearer token.
97
+ For Gmail, `POME_GMAIL_TOKEN` is the same Pome session JWT as `POME_AUTH_TOKEN`.
98
+ It is not a Google OAuth token.
99
+
100
+ Create a seed file, then use it to start a twin:
72
101
 
73
102
  ```bash
74
- pome init --example nope # the full list, each with what it teaches
103
+ pome twin new-seed github --out seed.json
104
+ pome twin start github --seed seed.json
75
105
  ```
76
106
 
77
- Two kinds are on that list: **agents for Pome to grade**, which you run with
78
- `pome run`, and **integration harnesses** (Braintrust, LangSmith) that drive Pome
79
- from their own eval runner and are started by it, not by `pome run`. The output
80
- tells you which one you scaffolded.
107
+ A supplied seed replaces the default seed.
108
+ It does not merge with the default seed.
109
+
110
+ ## Commands
111
+
112
+ | Command | Purpose |
113
+ | --- | --- |
114
+ | `pome init` | Write `pome.json` and, when applicable, starter files. |
115
+ | `pome login` | Sign in and store a hosted API key. |
116
+ | `pome logout` | Remove locally stored hosted credentials. |
117
+ | `pome docs [topic]` | Print or select a documentation URL. |
118
+ | `pome tasks [twin]` | List or copy bundled tasks. |
119
+ | `pome checks [twin]` | List the checks that can grade `[code]` criteria. |
120
+ | `pome checks add <file>` | Add one declared `[code]` criterion to a task. |
121
+ | `pome checks lint <file...>` | Report `[code]` criteria that do not bind to declared checks. |
122
+ | `pome compile-seeds [target]` | Compile prose seed state to `.seed.json` files with Claude. |
123
+ | `pome register agent <name>` | Register an agent and write its slug to `pome.json`. |
124
+ | `pome sandbox create` | Create a hosted sandbox. |
125
+ | `pome sandbox list` | List hosted sandboxes. |
126
+ | `pome sandbox stop <session-id>` | Stop a hosted sandbox. |
127
+ | `pome run [path]` | Run one task or all task files in a directory. Hosted is the default. |
128
+ | `pome doctor` | Check the manifest, twin routing, and egress controls. |
129
+ | `pome eval [run-dir]` | Upload recorded artifacts and request a hosted verdict. |
130
+ | `pome inspect <run>` | Print a trace and audit report. |
131
+ | `pome fix-prompt [target]` | Build a repair prompt from recorded traces and hosted verdicts. |
132
+ | `pome twin start [name]` | Start a standalone local twin. |
133
+ | `pome twin new-seed <name...>` | Print or write a starter seed file. |
134
+ | `pome twin status` | Check the last standalone twin and print its connection values. |
135
+
136
+ ## Environment Variables
137
+
138
+ Global hosted configuration:
139
+
140
+ | Variable | Purpose |
141
+ | --- | --- |
142
+ | `POME_API_KEY` | Authenticate hosted commands. This value takes precedence over stored credentials. |
143
+ | `POME_API_URL` | Set the control-plane URL. `--api-url` takes precedence. |
144
+ | `POME_DASHBOARD_URL` | Set the dashboard URL for login and result links. |
145
+
146
+ Agent process configuration:
147
+
148
+ | Variable | Purpose |
149
+ | --- | --- |
150
+ | `POME_AGENT_ENV_ALLOWLIST` | Add comma-separated parent variable names to the agent process. |
151
+ | `POME_EGRESS_ALLOW` | Add comma-separated host patterns to the capture proxy allowlist. |
152
+ | `POME_INHERIT_AGENT_ENV=1` | Pass the full parent environment to the agent. Use this only with trusted agents. |
153
+ | `POME_TRUST_AGENT_COMMAND=1` | Run the agent command through a shell. Use this only with trusted commands. |
81
154
 
82
- The files come from GitHub at the commit that built your CLI, so an example is
83
- always the one this version was released with. `POME_EXAMPLE_REF` overrides the
84
- ref when you want a branch.
155
+ The CLI passes these provider variables to the agent by default when they are set:
85
156
 
86
- See [docs.pome.sh](https://docs.pome.sh) for the task library, authentication,
87
- the Stripe/Slack twins, and everything else.
157
+ - `AI_GATEWAY_API_KEY`
158
+ - `ANTHROPIC_API_KEY`
159
+ - `CLAUDE_CODE_OAUTH_TOKEN`
160
+ - `GOOGLE_API_KEY`
161
+ - `GOOGLE_GENERATIVE_AI_API_KEY`
162
+ - `OPENAI_API_KEY`
163
+ - `OPENROUTER_API_KEY`
88
164
 
89
- ## CI one-shot the exit-code contract
165
+ `pome compile-seeds` requires `ANTHROPIC_API_KEY`.
90
166
 
91
- `pome run <task>` is the CI one-shot: one hosted, scored run, and its **exit
92
- code is the verdict**. Gate CI on it directly.
167
+ Standalone twin configuration:
93
168
 
94
- | Exit code | Meaning |
169
+ | Variable | Purpose |
95
170
  | --- | --- |
96
- | `0` | pass (hosted/scored run), or trace captured (`--local`, not scored) |
97
- | `1` | ran and scored **below** the pass threshold, **or ran `INCOMPLETE`** |
98
- | `2` | twin / orchestration error (network, 5xx, twin spawn failed) |
99
- | `3` | auth error (401/403) `pome login` again, or set `POME_API_KEY` in CI |
100
- | `4` | quota exceeded (402/429) |
101
- | `5` | usage error (bad flags, missing task file) |
102
-
103
- Three rules CI must honor:
104
-
105
- - **`--local` is not a verdict.** A `--local` run captures a raw trace and never
106
- scores, so its exit `0` means "trace captured," not "passed." Never gate CI on
107
- a `--local` exit code score it later with `pome eval <run-dir>`.
108
- - **`INCOMPLETE` shares exit `1`, and it is not the agent's failure.** A run
109
- whose criteria could not all be graded exits `1` rather than mapping its
110
- partial score to a code — a run whose checks never ran is not a green CI
111
- signal. The cost is stated rather than hidden: **`1` cannot tell "the agent
112
- regressed" from "we could not grade it."** To separate them programmatically,
113
- do not compare `score` against `pass_threshold` yourself — a run with a third
114
- of its criteria unevaluated can still read `score: 100, pass_threshold: 100`
115
- with nothing in those two fields alone saying so. Read `state` in the
116
- `verdict.json` a hosted `pome run` writes to
117
- `<artifacts-dir>/<task-slug>/<session-id>/verdict.json`: `"pass"`, `"fail"`,
118
- or `"incomplete"` the same word the terminal prints beside the score, and
119
- the field to gate on. The `evaluated` / `not_evaluated` / `pre_satisfied` /
120
- `total` counts alongside it say how much of the task `score` covers:
121
- **`score` is a percentage over `evaluated` alone**, so `not_evaluated > 0`
122
- means `score` is silent about part of the run, and `evaluated: 0` means it
123
- scored nothing at all (the cloud sends `0` there for want of a denominator
124
- "nothing was scored", not "nothing was correct").
125
- - **Trial groups map as a whole.** `pome run -n k` (k>1) collapses the whole
126
- group to one code: `0` = at least one trial completed and every completed
127
- trial passed; `1` = at least one completed trial failed its threshold **or was
128
- incomplete**; `2` = no trial completed. Errored and incomplete trials are
129
- excluded from the verdict fraction (`3 of 4 passed · 1 incomplete`) so neither
130
- is counted as a pass nor charged to the agent as a loss — but a group holding
131
- one cannot exit `0`.
132
- - **`pome fix-prompt` uses the same codes, and its `1` is only ever
133
- INCOMPLETE.** Building a prompt for a failed run set exits `0` (the prompt is
134
- on stdout, and stdout being non-empty is the signal that there was something
135
- to fix); an all-green root exits `0` with nothing on stdout; a bad argument or
136
- a root with no readable run sets exits `5`. `1` is reserved for the one case
137
- where the newest non-passing set was never fully graded: no prompt is built,
138
- because a run whose checks never ran is not evidence of an agent defect. This
139
- matches `pome run`, where `1` also covers INCOMPLETE — the two commands do not
140
- disagree about what an ungraded run exits, and `verdict.json`'s `state` stays
141
- the field to read when a script needs the reason rather than the code.
142
-
143
- ## Development
171
+ | `PORT` | Set the listen port for `pome twin start`. `--port` takes precedence. |
172
+ | `POME_SEED_JSON` | Supply seed JSON. `--seed` takes precedence. |
173
+ | `TWIN_AUTH_SECRET` | Supply the secret that signs local session JWTs. |
174
+ | `POME_TWIN_DATA_DIR` | Set the directory for the persisted twin secret. |
175
+
176
+ The twin entry points also accept their provider-specific host, port, database, and no-seed variables.
177
+ See [`CONTRACT.md`](../CONTRACT.md) for that runtime interface.
178
+
179
+ ## Artifacts
180
+
181
+ The default artifact root is `runs/`.
182
+ Use the global `--artifacts-dir <dir>` option to select another root.
183
+
184
+ Each completed run uses this directory format:
185
+
186
+ ```text
187
+ <artifacts-dir>/<task-slug>/<run-id>/
188
+ ```
189
+
190
+ The six core files are:
191
+
192
+ | File | Content |
193
+ | --- | --- |
194
+ | `meta.json` | Run identity, times, agent exit, twins, and format versions. |
195
+ | `events.jsonl` | Redacted twin, model, and adapter events. |
196
+ | `state_initial.json` | Initial state for the primary twin. |
197
+ | `state_final.json` | Final state for the primary twin. |
198
+ | `stdout.txt` | Redacted agent standard output. |
199
+ | `stderr.log` | Redacted agent standard error. |
200
+
201
+ A run can also contain these files:
202
+
203
+ | File | Condition |
204
+ | --- | --- |
205
+ | `signals.jsonl` | The runner creates this adapter-event sidecar. |
206
+ | `egress.jsonl` | The capture proxy records refused connections here. |
207
+ | `state_final.<twin>.json` | A multi-twin run records each additional final state. |
208
+ | `verdict.json` | A hosted `pome run` caches the hosted verdict for `pome fix-prompt`. |
209
+ | `eval-session.json` | `pome eval` records the hosted evaluation session for safe reuse. |
210
+
211
+ The artifact root also contains `latest.json`.
212
+ It points to the most recent run directory.
213
+
214
+ The CLI never writes `score.json`.
215
+ A hosted verdict comes from Pome.
216
+
217
+ ## Exit Codes
218
+
219
+ ### Hosted `pome run` And `pome eval`
220
+
221
+ | Code | Meaning |
222
+ | --- | --- |
223
+ | `0` | The hosted verdict is `pass`. |
224
+ | `1` | The hosted verdict is `fail` or `incomplete`. |
225
+ | `2` | A twin, missing agent command, malformed task configuration, network, or orchestration error prevented a verdict. |
226
+ | `3` | Authentication failed. |
227
+ | `4` | The account exceeded a quota. |
228
+ | `5` | The CLI rejected the invocation during validation, for example because a path, option, or option combination is invalid. |
229
+
230
+ For `pome run`, the task supplies the pass threshold.
231
+ For `pome eval`, the pass threshold is `100`.
232
+
233
+ For a hosted `pome run`, read `state` in `verdict.json` to distinguish `fail` from `incomplete`.
234
+ The possible values are `"pass"`, `"fail"`, and `"incomplete"`.
235
+
236
+ The `evaluated`, `not_evaluated`, `pre_satisfied`, and `total` fields describe grading coverage.
237
+ `score` is a percentage over `evaluated` criteria only.
238
+ Thus, `not_evaluated > 0` means that the score does not cover the complete task.
239
+
240
+ ### Hosted Trial Groups
241
+
242
+ `pome run -n k` returns one code for the group when `k` is greater than `1`.
243
+
244
+ | Code | Meaning |
245
+ | --- | --- |
246
+ | `0` | At least one trial completed and every completed trial passed. |
247
+ | `1` | At least one completed trial failed or was incomplete. |
248
+ | `2` | No trial completed. |
249
+
250
+ Errored trials do not enter the verdict fraction.
251
+ An incomplete trial also prevents exit `0`.
252
+
253
+ ### Local `pome run --local`
254
+
255
+ | Code | Meaning |
256
+ | --- | --- |
257
+ | `0` | The agent completed and the CLI recorded the trace. This code is not a verdict. |
258
+ | `2` | A local twin, missing agent command, malformed task configuration, or runner error prevented capture. |
259
+ | `3` | The agent failed, timed out, or failed its preflight. |
260
+ | `5` | The CLI rejected the invocation during validation, for example because a path, option, or option combination is invalid. |
261
+
262
+ Do not use a local exit `0` as a CI quality gate.
263
+ Run `pome eval <run-dir>` to request a verdict.
264
+
265
+ ### `pome fix-prompt`
266
+
267
+ - Exit `0` means that the command succeeded. Standard output can be empty when all run sets passed.
268
+ - Exit `1` means that the newest non-passing set is incomplete.
269
+ - Exit `5` means that the target or arguments are invalid.
270
+
271
+ ## Contribute
272
+
273
+ Run these commands from the repository root:
144
274
 
145
275
  ```bash
146
276
  npm install
147
- npm run typecheck
148
277
  npm run build
278
+ npm run typecheck
149
279
  npx vitest run --project cli
150
280
  ```
151
281
 
152
- The package publishes the `pome` binary from `dist/src/cli/main.js`.
153
-
154
- ### Versioning — every behavior change ships with a release, and you do not write the number
282
+ Use `npm run test:contract` for the packaged twin runtime contract.
283
+ Run `node --test contract/cli-start.test.mjs` after you build the CLI front door.
155
284
 
156
- Add the user-facing entry to `CHANGELOG.md` under an `## Unreleased (patch)` (or
157
- `(minor)`) heading, above the newest released one, and leave `version` in
158
- `cli/package.json` alone — a PR that moves it fails CI. Merging to `main` is the
159
- release trigger: `.github/workflows/allocate-version.yml` allocates the number
160
- there, rewriting that heading and the manifest in one commit, and
161
- `.github/workflows/release.yml` compares the local version against npm and
162
- publishes when they differ. `pome --version` reports the allocated value from a
163
- build-time constant, so a user can always tell whether their install carries a
164
- given fix.
285
+ The package publishes `pome` from `cli/dist/src/cli/main.js`.
165
286
 
166
287
  ## License
167
288
 
168
- Apache-2.0. See [`LICENSE`](./LICENSE).
289
+ Apache-2.0. See [`LICENSE`](../LICENSE).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "pome-sh",
3
- "version": "0.43.0",
4
- "git_sha": "589885b59adab9a5fded977620a6adc25125da85",
5
- "build_time": "2026-09-01T07:18:35.031Z"
3
+ "version": "0.43.1",
4
+ "git_sha": "aa6e8ff0d76f2bfbdd8c01e72239431644cb849a",
5
+ "build_time": "2026-09-15T22:37:32.106Z"
6
6
  }
@@ -182,7 +182,7 @@ async function checkTwinReachable(_configDir) {
182
182
  });
183
183
  let harness;
184
184
  try {
185
- const { bootTwin } = await import('./twinHarness-WJFDLEAO.js');
185
+ const { bootTwin } = await import('./twinHarness-33MAVAXV.js');
186
186
  harness = await bootTwin({
187
187
  twin: "github",
188
188
  seedState: void 0,
@@ -39,7 +39,7 @@ var TWIN_REGISTRY = {
39
39
  defaultSeedState,
40
40
  GitHubDomain,
41
41
  openGitHubCloneDatabase
42
- } = await import('./src-E7NTZL2F.js');
42
+ } = await import('./src-G6CMFUR6.js');
43
43
  const db = openGitHubCloneDatabase();
44
44
  const domain = new GitHubDomain(db);
45
45
  domain.seed(seedState === void 0 ? defaultSeedState() : seedState);
@@ -63,7 +63,7 @@ var TWIN_REGISTRY = {
63
63
  parseSeed: async (input) => (await import('./seed-SYJXLGWF.js')).parseSeed(input),
64
64
  seedFields: async () => Object.keys((await import('./seed-SYJXLGWF.js')).seedSchema.shape),
65
65
  async boot({ seedState, runId, recorder }) {
66
- const { createSlackTwinApp, openSlackTwinDatabase, SlackDomain } = await import('./src-7X62AGW7.js');
66
+ const { createSlackTwinApp, openSlackTwinDatabase, SlackDomain } = await import('./src-SJDGRXC5.js');
67
67
  const db = openSlackTwinDatabase(":memory:");
68
68
  const domain = new SlackDomain(db);
69
69
  domain.applySeed(seedState);
@@ -90,8 +90,8 @@ var TWIN_REGISTRY = {
90
90
  parseSeed: async (input) => (await import('./seed-E6SA4NKZ.js')).parseSeed(input),
91
91
  seedFields: async () => Object.keys((await import('./seed-E6SA4NKZ.js')).seedSchema.shape),
92
92
  async boot({ seedState, runId, recorder, twinBaseUrl }) {
93
- const stripeTwin = await import('./src-ESRY2MC7.js');
94
- const { createApp } = await import('./server-KC57K5AC.js');
93
+ const stripeTwin = await import('./src-VBXONWR3.js');
94
+ const { createApp } = await import('./server-2WHSHTUD.js');
95
95
  const {
96
96
  applySeed: applyStripeSeed,
97
97
  createTwinStripeApp,
@@ -136,7 +136,7 @@ var TWIN_REGISTRY = {
136
136
  parseSeed: async (input) => (await import('./seed-W3R53GHH.js')).parseSeed(input),
137
137
  seedFields: async () => Object.keys((await import('./seed-W3R53GHH.js')).gmailSeedSchema.shape),
138
138
  async boot({ seedState, runId, recorder }) {
139
- const { createGmailTwinApp, GmailDomain, openGmailTwinDatabase, parseSeed } = await import('./src-Z63IOSCJ.js');
139
+ const { createGmailTwinApp, GmailDomain, openGmailTwinDatabase, parseSeed } = await import('./src-DBOHIQNB.js');
140
140
  const db = openGmailTwinDatabase(":memory:");
141
141
  const seed = parseSeed(seedState);
142
142
  const domain = new GmailDomain(db);
@@ -165,7 +165,7 @@ var TWIN_REGISTRY = {
165
165
  LinearDomain,
166
166
  openLinearTwinDatabase,
167
167
  parseSeed
168
- } = await import('./src-5F5OTVYT.js');
168
+ } = await import('./src-FRPUTB2H.js');
169
169
  const db = openLinearTwinDatabase(":memory:");
170
170
  const seed = parseSeed(seedState);
171
171
  const domain = new LinearDomain(db);
@@ -181,7 +181,7 @@ var TWIN_REGISTRY = {
181
181
  }
182
182
  };
183
183
  async function createGitHubSmokeApp() {
184
- const { createGitHubCloneApp } = await import('./src-E7NTZL2F.js');
184
+ const { createGitHubCloneApp } = await import('./src-G6CMFUR6.js');
185
185
  return createGitHubCloneApp();
186
186
  }
187
187
  function defaultPortFor(twin, env = process.env) {
@@ -1,5 +1,5 @@
1
- import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-ELIEDNF3.js';
2
- import { createFileBackedRecorderStore, createRecorderStore } from './chunk-HRAD7MRX.js';
1
+ import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-4DXTT3RV.js';
2
+ import { createFileBackedRecorderStore, createRecorderStore } from './chunk-TWURH7YM.js';
3
3
 
4
4
  // src/recorder/recorder.ts
5
5
  function createRecorder(options = {}) {
@@ -1,11 +1,11 @@
1
- import { readSeedFileText, parseSeedFileText, soleTwinOf, twinsNamedBy, seedsForTwins } from './chunk-3YCX3KUL.js';
1
+ import { readSeedFileText, parseSeedFileText, soleTwinOf, twinsNamedBy, seedsForTwins } from './chunk-GQSNU3YP.js';
2
2
  import { gmailSeedSchema, defaultSeedState } from './chunk-PASFBRK4.js';
3
3
  import { linearSeedSchema, defaultSeedState as defaultSeedState$1 } from './chunk-3FZY376K.js';
4
- import { isTwinName, TWIN_REGISTRY } from './chunk-ELIEDNF3.js';
4
+ import { isTwinName, TWIN_REGISTRY } from './chunk-4DXTT3RV.js';
5
5
  import { criterionSchema, normalizeTaskConfigKeys, finalizeResponseSchema, MOUNTED_TWINS, HostedDiscardRefusedError, HostedOrchError, HostedAuthError, readManifest, normalizeManifestTwins, HostedQuotaError, HostedTrialError, submitResultResponseSchema, createEvalSessionResponseSchema, createSessionResponseSchema, agentResponseSchema, isMultiTwinSeedEnvelope, sessionPublicSchema } from './chunk-CS7O2ZXB.js';
6
6
  import { seedSchema as seedSchema$1, parseSeed, defaultSeedState as defaultSeedState$2 } from './chunk-NBOQN5VX.js';
7
7
  import { seedSchema } from './chunk-YBWG5JK2.js';
8
- import { toTwinHttpEventRow } from './chunk-HRAD7MRX.js';
8
+ import { toTwinHttpEventRow } from './chunk-TWURH7YM.js';
9
9
  import { redactEvent, redactSecrets } from './chunk-SG6ZTIMT.js';
10
10
  import { seedSchema as seedSchema$2 } from './chunk-2K6BJ3PI.js';
11
11
  import { mkdir, appendFile, writeFile, readFile, rm, stat, chmod, mkdtemp, readdir, rename } from 'node:fs/promises';
@@ -1,4 +1,4 @@
1
- import { isTwinName, TWIN_REGISTRY, TWIN_NAMES } from './chunk-ELIEDNF3.js';
1
+ import { isTwinName, TWIN_REGISTRY, TWIN_NAMES } from './chunk-4DXTT3RV.js';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { parse } from 'yaml';
4
4
 
@@ -445,6 +445,7 @@ function defineTwin(spec) {
445
445
  }
446
446
  var RESERVED_SESSION_PREFIXES = ["/_pome", "/mcp"];
447
447
  var CLIENT_IP_VAR = "pomeClientIp";
448
+ var ADMIN_NO_PEER_OPT_IN = "TWIN_ADMIN_ALLOW_NO_PEER";
448
449
  var nodeGetConnInfo;
449
450
  function loadNodeGetConnInfo() {
450
451
  nodeGetConnInfo ??= import('@hono/node-server/conninfo').then((mod) => mod.getConnInfo, () => void 0);
@@ -490,10 +491,11 @@ function createAdminGate(options = {}) {
490
491
  }
491
492
  const remote = await getClientIp(c);
492
493
  if (!remote) {
493
- if (process.env.NODE_ENV === "production")
494
- return forbidden();
495
- await next();
496
- return;
494
+ if (process.env[ADMIN_NO_PEER_OPT_IN] === "1") {
495
+ await next();
496
+ return;
497
+ }
498
+ return forbidden();
497
499
  }
498
500
  if (!isLoopbackAddress(remote))
499
501
  return forbidden();
@@ -503,13 +505,22 @@ function createAdminGate(options = {}) {
503
505
 
504
506
  // ../packages/sdk/dist/auth.js
505
507
  var PROVIDER_SHAPED_TEAM_ID = "provider-shaped";
508
+ var DEV_ONLY_INSECURE_SECRET = "dev-only-insecure-secret";
509
+ var DEV_SECRETS_OPT_IN = "POME_ALLOW_DEV_SECRETS";
506
510
  function resolveAuthSecret() {
507
511
  const secret = process.env.TWIN_AUTH_SECRET;
508
- if (!secret && process.env.NODE_ENV === "production") {
509
- throw new Error("TWIN_AUTH_SECRET required in production");
510
- }
511
- return secret ?? "dev-only-insecure-secret";
512
+ if (secret)
513
+ return secret;
514
+ if (process.env[DEV_SECRETS_OPT_IN] === "1")
515
+ return DEV_ONLY_INSECURE_SECRET;
516
+ throw new MissingAuthSecretError();
512
517
  }
518
+ var MissingAuthSecretError = class extends Error {
519
+ constructor() {
520
+ super(`TWIN_AUTH_SECRET is not set. Set it, or set ${DEV_SECRETS_OPT_IN}=1 to serve the public dev secret on a twin nothing but this machine can reach.`);
521
+ this.name = "MissingAuthSecretError";
522
+ }
523
+ };
513
524
  var SIG_LENGTH = 22;
514
525
  function mintProviderToken(spec, options) {
515
526
  const prefix = options.prefix ?? spec.prefixes[0];
@@ -674,7 +685,14 @@ function bearerAuth(options = {}) {
674
685
  }
675
686
  }
676
687
  if (options.providerToken) {
677
- const providerSid = verifyProviderToken(options.providerToken, token);
688
+ let providerSid;
689
+ try {
690
+ providerSid = verifyProviderToken(options.providerToken, token);
691
+ } catch (err) {
692
+ if (err instanceof MissingAuthSecretError)
693
+ return respond(unauthorized("invalid", { token }));
694
+ throw err;
695
+ }
678
696
  if (providerSid) {
679
697
  const mismatch2 = checkSid(providerSid);
680
698
  if (mismatch2)
@@ -1281,8 +1299,14 @@ function isLoopbackHost(value) {
1281
1299
  function ensureTwinAuthSecret(twin, host) {
1282
1300
  if (process.env.TWIN_AUTH_SECRET)
1283
1301
  return;
1284
- if (isLoopbackHost(host))
1302
+ if (isLoopbackHost(host)) {
1303
+ if (process.env[DEV_SECRETS_OPT_IN] === "1")
1304
+ return;
1305
+ const secret = randomBytes(32).toString("hex");
1306
+ process.env.TWIN_AUTH_SECRET = secret;
1307
+ console.log(`[twin-${twin}] TWIN_AUTH_SECRET not set \u2014 generated ${secret} for this loopback boot (not persisted; set TWIN_AUTH_SECRET to choose one, or ${DEV_SECRETS_OPT_IN}=1 for the public dev secret)`);
1285
1308
  return;
1309
+ }
1286
1310
  const dataDir = process.env.POME_TWIN_DATA_DIR || join(".pome-data", twin);
1287
1311
  const secretPath = join(dataDir, "secret");
1288
1312
  try {
@@ -1,13 +1,13 @@
1
- import { runTaskHosted, createHostedClient, parseTaskFile, resolveRunAgentIdentity, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-OBFHOACQ.js';
2
- import './chunk-3YCX3KUL.js';
1
+ import { runTaskHosted, createHostedClient, parseTaskFile, resolveRunAgentIdentity, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-A6AM6KS4.js';
2
+ import './chunk-GQSNU3YP.js';
3
3
  import './chunk-NW7HGA2K.js';
4
4
  import './chunk-PASFBRK4.js';
5
5
  import './chunk-3FZY376K.js';
6
- import './chunk-ELIEDNF3.js';
6
+ import './chunk-4DXTT3RV.js';
7
7
  import { HostedQuotaError, HostedTrialError } from './chunk-CS7O2ZXB.js';
8
8
  import './chunk-NBOQN5VX.js';
9
9
  import './chunk-YBWG5JK2.js';
10
- import './chunk-HRAD7MRX.js';
10
+ import './chunk-TWURH7YM.js';
11
11
  import './chunk-5KFDRR53.js';
12
12
  import './chunk-SG6ZTIMT.js';
13
13
  import './chunk-2K6BJ3PI.js';
@@ -1,4 +1,4 @@
1
- export { POME_RECORDER_EVENTS_PATH, PROVIDER_SHAPED_TEAM_ID, TwinBootError, TwinError, UnknownToolError, bearerAuth, createAdminGate, createApp, createFileBackedRecorderStore, createRecorderHandle, createRecorderStore, created, ensureTwinAuthSecret, failureInjectionMiddleware, formTokenResolver, isLoopbackHost, mintProviderToken, ok, queryTokenResolver, recordedRequestHeaders, requireAdminAuth, resolveAuthSecret, resolveRecorderStore, serve, setClientIp, setRecordedTool, toTwinHttpEventRow, twinBuildInfo, verifyProviderToken } from './chunk-HRAD7MRX.js';
1
+ export { POME_RECORDER_EVENTS_PATH, PROVIDER_SHAPED_TEAM_ID, TwinBootError, TwinError, UnknownToolError, bearerAuth, createAdminGate, createApp, createFileBackedRecorderStore, createRecorderHandle, createRecorderStore, created, ensureTwinAuthSecret, failureInjectionMiddleware, formTokenResolver, isLoopbackHost, mintProviderToken, ok, queryTokenResolver, recordedRequestHeaders, requireAdminAuth, resolveAuthSecret, resolveRecorderStore, serve, setClientIp, setRecordedTool, toTwinHttpEventRow, twinBuildInfo, verifyProviderToken } from './chunk-TWURH7YM.js';
2
2
  import './chunk-5KFDRR53.js';
3
3
  export { redactEvent, redactSecrets } from './chunk-SG6ZTIMT.js';
4
4
  export { FAILURE_INJECTION_OVERRIDE_KEY, createFailureInjectionStore, failureInjectionRuleSchema } from './chunk-FBSA5L36.js';
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, parseTaskFile, runTaskHosted, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, discoverRunSet, outcomeOf, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, readConfigTwins, resolveCachedAgentId, readLinkCache, seedStateForTwin, runAgentCommand, scoreCountsSummary, criterionRowLine, readCodeCriteria, postAgentResolver, writeLinkCache, ensurePomeGitignored, writeRunArtifactsCore, createHostedClient, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-OBFHOACQ.js';
3
- import '../../chunk-3YCX3KUL.js';
2
+ import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, parseTaskFile, runTaskHosted, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, discoverRunSet, outcomeOf, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, readConfigTwins, resolveCachedAgentId, readLinkCache, seedStateForTwin, runAgentCommand, scoreCountsSummary, criterionRowLine, readCodeCriteria, postAgentResolver, writeLinkCache, ensurePomeGitignored, writeRunArtifactsCore, createHostedClient, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-A6AM6KS4.js';
3
+ import '../../chunk-GQSNU3YP.js';
4
4
  import '../../chunk-NW7HGA2K.js';
5
5
  import { GMAIL_CHECKS } from '../../chunk-JM6VS62R.js';
6
6
  import '../../chunk-PASFBRK4.js';
7
7
  import { LINEAR_CHECKS } from '../../chunk-TSOSDKXP.js';
8
8
  import '../../chunk-3FZY376K.js';
9
- import { createRecorder, bootTwin } from '../../chunk-5A6HHA54.js';
10
- import { TWIN_NAME_LIST, TWIN_REGISTRY, createGitHubSmokeApp } from '../../chunk-ELIEDNF3.js';
9
+ import { createRecorder, bootTwin } from '../../chunk-64AWQJ7R.js';
10
+ import { TWIN_NAME_LIST, TWIN_REGISTRY, createGitHubSmokeApp } from '../../chunk-4DXTT3RV.js';
11
11
  import { getAvailablePort } from '../../chunk-XDU6TD4O.js';
12
12
  import { MOUNTED_TWINS, readManifest, deriveAgentSlug, writeManifest, MANIFEST_JSON, exitCodeFor, readRequiredManifest, HostedUsageError, HostedOrchError, normalizeManifestTwins } from '../../chunk-CS7O2ZXB.js';
13
13
  import { buildEgressAllowlist, readBlockedEgress } from '../../chunk-CBFKZZBR.js';
@@ -16,7 +16,7 @@ import { seedSchema } from '../../chunk-NBOQN5VX.js';
16
16
  import { SLACK_CHECKS } from '../../chunk-2ZGVDTJC.js';
17
17
  import { oneOf, defineCheck, repoRef, VACUITY_SENTINEL_NUMBER, childStatePath, VACUITY_SENTINEL, statePath, templateSlots, renderCheck, checksDigest, checkPattern, checkNearMissPattern } from '../../chunk-JWJYNAWI.js';
18
18
  import '../../chunk-YBWG5JK2.js';
19
- import '../../chunk-HRAD7MRX.js';
19
+ import '../../chunk-TWURH7YM.js';
20
20
  import { eventSchema, isLegacyEventRow } from '../../chunk-5KFDRR53.js';
21
21
  import { redactSecrets, redactEvent } from '../../chunk-SG6ZTIMT.js';
22
22
  import '../../chunk-2K6BJ3PI.js';
@@ -1187,7 +1187,7 @@ function extractJsonPayload(text) {
1187
1187
 
1188
1188
  // src/task/seed-verifier.ts
1189
1189
  async function verifySeedWithTwin(seed) {
1190
- const { GitHubDomain, openGitHubCloneDatabase } = await import('../../src-E7NTZL2F.js');
1190
+ const { GitHubDomain, openGitHubCloneDatabase } = await import('../../src-G6CMFUR6.js');
1191
1191
  const db = openGitHubCloneDatabase(":memory:");
1192
1192
  try {
1193
1193
  new GitHubDomain(db).seed(seed);
@@ -3395,7 +3395,7 @@ function localDigest(twin) {
3395
3395
  }
3396
3396
  function bakedVersions() {
3397
3397
  try {
3398
- return JSON.parse('{"@pome-sh/sdk":"0.11.6","@pome-sh/wire":"0.4.1","@pome-sh/twin-github":"0.12.0","@pome-sh/twin-gmail":"0.4.0","@pome-sh/twin-linear":"0.4.1","@pome-sh/twin-slack":"0.4.1","@pome-sh/twin-stripe":"0.4.7"}');
3398
+ return JSON.parse('{"@pome-sh/sdk":"0.11.6","@pome-sh/wire":"0.4.2","@pome-sh/twin-github":"0.12.0","@pome-sh/twin-gmail":"0.4.0","@pome-sh/twin-linear":"0.4.1","@pome-sh/twin-slack":"0.4.1","@pome-sh/twin-stripe":"0.4.7"}');
3399
3399
  } catch {
3400
3400
  return {};
3401
3401
  }
@@ -4961,7 +4961,7 @@ function firstSentence(description) {
4961
4961
  function resolveExampleRef(env = process.env) {
4962
4962
  const override = env.POME_EXAMPLE_REF?.trim();
4963
4963
  if (override) return override;
4964
- const baked = "589885b59adab9a5fded977620a6adc25125da85".trim() ;
4964
+ const baked = "aa6e8ff0d76f2bfbdd8c01e72239431644cb849a".trim() ;
4965
4965
  return FULL_SHA.test(baked) ? baked : "main";
4966
4966
  }
4967
4967
  function rawUrlFor(example, file, ref) {
@@ -5324,7 +5324,7 @@ var DEFAULT_AGENT_COMMAND = `node ${DEFAULT_AGENT_FILE}`;
5324
5324
  var MANIFEST_SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
5325
5325
  var MAX_UNREADABLE_PATHS_SHOWN = 5;
5326
5326
  function readPackageVersion() {
5327
- if ("0.43.0".length > 0) return "0.43.0";
5327
+ if ("0.43.1".length > 0) return "0.43.1";
5328
5328
  try {
5329
5329
  const here = dirname(fileURLToPath(import.meta.url));
5330
5330
  const candidates = [
@@ -5728,7 +5728,7 @@ function createProgram() {
5728
5728
  return;
5729
5729
  }
5730
5730
  {
5731
- const { runDoctorChecks } = await import('../../checks-QYUPKBPT.js');
5731
+ const { runDoctorChecks } = await import('../../checks-V442KL5O.js');
5732
5732
  const { renderDoctorReport } = await import('../../render-ZQQ4UMNO.js');
5733
5733
  const doctorReport = await runDoctorChecks({ mode: useLocal ? "full" : "hosted" });
5734
5734
  if (!doctorReport.ok) {
@@ -5766,7 +5766,7 @@ function createProgram() {
5766
5766
  taskForRuns.config.runs
5767
5767
  );
5768
5768
  if (k > 1) {
5769
- const { runTrialGroup } = await import('../../runTrialGroup-4QL6S7QO.js');
5769
+ const { runTrialGroup } = await import('../../runTrialGroup-HYGGBLMM.js');
5770
5770
  const fileForRerun = relative(process.cwd(), file);
5771
5771
  const rerunCommand = defaultTask ? options.trials !== void 0 ? `pome run -n ${k}` : "pome run" : `pome run ${fileForRerun && !fileForRerun.startsWith("..") ? fileForRerun : file} -n ${k}`;
5772
5772
  const groupResult = await runTrialGroup({
@@ -5848,7 +5848,7 @@ function createProgram() {
5848
5848
  program.command("doctor").summary("Check the agent and twin wiring").description(
5849
5849
  "Check the agent\u2194twin wiring: pome.json (or pome.yaml) present + valid, the local twin boots + serves, requests routed to the twin (not a hardcoded production host), egress floor active. On failure prints one named cause (file:line where knowable) + one concrete fix and exits non-zero."
5850
5850
  ).action(async () => {
5851
- const { runDoctorChecks } = await import('../../checks-QYUPKBPT.js');
5851
+ const { runDoctorChecks } = await import('../../checks-V442KL5O.js');
5852
5852
  const { renderDoctorReport } = await import('../../render-ZQQ4UMNO.js');
5853
5853
  const report = await runDoctorChecks();
5854
5854
  for (const line of renderDoctorReport(report, { passNote: true })) console.error(line);
@@ -6016,13 +6016,13 @@ function createProgram() {
6016
6016
  ).description(
6017
6017
  "Start a standalone twin as a long-lived foreground server (Ctrl-C to stop)"
6018
6018
  ).action(async (name, options) => {
6019
- const { runTwinStartCommand } = await import('../../twinStart-QQ64EV3P.js');
6019
+ const { runTwinStartCommand } = await import('../../twinStart-3Q74IURZ.js');
6020
6020
  await runTwinStartCommand(name, options);
6021
6021
  });
6022
6022
  twin.command("new-seed").argument("<name...>", `Twin name (${TWIN_NAME_LIST.join(" | ")}). Repeat for one file covering several.`).option("--out <path>", "Write to this file instead of stdout. Refuses to overwrite.").summary("Print a new starter seed file for a twin").description(
6023
6023
  "Print a new starter seed file for a twin, generated from the twin's own starting state. One twin is flat, several are the per-twin envelope. Boot it with `twin start <twin> --seed`, seed a sandbox with `sandbox create --twin <twin> --seed`, or drop it beside a task as <task>.seed.json"
6024
6024
  ).action(async (names, options) => {
6025
- const { runTwinSeedCommand } = await import('../../twinSeed-4VQ7RRCL.js');
6025
+ const { runTwinSeedCommand } = await import('../../twinSeed-J7UAY3GM.js');
6026
6026
  await runTwinSeedCommand(names, options);
6027
6027
  });
6028
6028
  twin.command("status").summary("Say whether the local twin is running").description(
@@ -3,7 +3,7 @@ import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFo
3
3
  export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-PASFBRK4.js';
4
4
  import './chunk-JWJYNAWI.js';
5
5
  import { routeInputDeclarer, integerInput, booleanInput, repeatedInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-HRAD7MRX.js';
6
+ import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-TWURH7YM.js';
7
7
  import './chunk-5KFDRR53.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
@@ -3,7 +3,7 @@ import { MCP_PAGE_MAX, defaultSeedState, linearSeedSchema, notFound, badUserInpu
3
3
  export { DEFAULT_LINEAR_CLOCK, DEFAULT_LINEAR_EMAIL, DEFAULT_LINEAR_PORT, DEFAULT_LINEAR_SID, DEFAULT_LINEAR_TOKEN, LINEAR_PROVIDER_TOKEN_PREFIX, LinearTwinError, STATE_EXPORT_CAP, assertWebhookUrl, defaultSeedState, linearErrorEnvelope, linearSeedSchema, loadSeedFromEnv, parseSeed, unauthorizedEnvelope, unsupportedEnvelope, webhookUrlError } from './chunk-3FZY376K.js';
4
4
  import './chunk-JWJYNAWI.js';
5
5
  import { routeInputDeclarer, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, deriveMcpToolTable, openTwinDatabase, defineTwin, createApp } from './chunk-HRAD7MRX.js';
6
+ import { loadMcpToolFixture, deriveMcpToolTable, openTwinDatabase, defineTwin, createApp } from './chunk-TWURH7YM.js';
7
7
  import './chunk-5KFDRR53.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
@@ -2,7 +2,7 @@ import './chunk-C4BVTUA3.js';
2
2
  import { defaultSeedState, parseSeed } from './chunk-NBOQN5VX.js';
3
3
  export { defaultSeedState, parseSeed, seedSchema } from './chunk-NBOQN5VX.js';
4
4
  import { routeInputDeclarer, integerInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
5
- import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
5
+ import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-TWURH7YM.js';
6
6
  import './chunk-5KFDRR53.js';
7
7
  import './chunk-SG6ZTIMT.js';
8
8
  import './chunk-FBSA5L36.js';
@@ -3,7 +3,7 @@ import './chunk-JWJYNAWI.js';
3
3
  import { defaultSeedState, parseSeed } from './chunk-YBWG5JK2.js';
4
4
  export { defaultSeedState, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-YBWG5JK2.js';
5
5
  import { routeInputDeclarer, booleanInput, integerInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
6
+ import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-TWURH7YM.js';
7
7
  import './chunk-5KFDRR53.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
@@ -1,5 +1,5 @@
1
1
  import { routeInputDeclarer, bracketedQuery, integerInput, booleanInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
2
- import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-HRAD7MRX.js';
2
+ import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-TWURH7YM.js';
3
3
  import './chunk-5KFDRR53.js';
4
4
  import './chunk-SG6ZTIMT.js';
5
5
  import { defaultSeed, seedSchema } from './chunk-2K6BJ3PI.js';
@@ -0,0 +1,6 @@
1
+ export { UnsupportedTwinError, bootTwin } from './chunk-64AWQJ7R.js';
2
+ export { STRIPE_LOCAL_ACCOUNT_ID } from './chunk-4DXTT3RV.js';
3
+ import './chunk-TWURH7YM.js';
4
+ import './chunk-5KFDRR53.js';
5
+ import './chunk-SG6ZTIMT.js';
6
+ import './chunk-FBSA5L36.js';
@@ -1,4 +1,4 @@
1
- import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-ELIEDNF3.js';
1
+ import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-4DXTT3RV.js';
2
2
  import { writeFile } from 'node:fs/promises';
3
3
 
4
4
  async function generateSeedFile(twins) {
@@ -1,18 +1,24 @@
1
- import { readSeedFileText, seedForTwin, parseSeedFileText, soleTwinOf, twinsNamedBy } from './chunk-3YCX3KUL.js';
2
- import { bootTwin } from './chunk-5A6HHA54.js';
3
- import { TWIN_REGISTRY, isTwinName, TWIN_NAMES, defaultPortFor } from './chunk-ELIEDNF3.js';
4
- import './chunk-HRAD7MRX.js';
1
+ import { readSeedFileText, seedForTwin, parseSeedFileText, soleTwinOf, twinsNamedBy } from './chunk-GQSNU3YP.js';
2
+ import { bootTwin } from './chunk-64AWQJ7R.js';
3
+ import { TWIN_REGISTRY, isTwinName, TWIN_NAMES, defaultPortFor } from './chunk-4DXTT3RV.js';
4
+ import './chunk-TWURH7YM.js';
5
5
  import './chunk-5KFDRR53.js';
6
6
  import './chunk-SG6ZTIMT.js';
7
7
  import './chunk-FBSA5L36.js';
8
8
  import { randomBytes } from 'node:crypto';
9
9
  import { readFileSync } from 'node:fs';
10
- import { mkdir, writeFile } from 'node:fs/promises';
11
- import { join } from 'node:path';
10
+ import { mkdir, writeFile, chmod } from 'node:fs/promises';
11
+ import { dirname, join } from 'node:path';
12
12
  import { serve } from '@hono/node-server';
13
13
  import { sign } from 'hono/jwt';
14
14
 
15
15
  var STANDALONE_SID = "standalone";
16
+ var STANDALONE_STATUS_PATH = ".pome/twin-status.json";
17
+ async function writeStandaloneStatusFile(status, path = STANDALONE_STATUS_PATH) {
18
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
19
+ await writeFile(path, JSON.stringify(status, null, 2), { mode: 384 });
20
+ await chmod(path, 384);
21
+ }
16
22
  function resolveStandaloneAuthSecret(twin, env = process.env) {
17
23
  const injected = env.TWIN_AUTH_SECRET;
18
24
  if (injected) return { secret: injected, source: "env" };
@@ -129,15 +135,13 @@ async function runTwinStartCommand(nameArg, options) {
129
135
  resolve();
130
136
  });
131
137
  });
132
- await mkdir(".pome", { recursive: true });
133
- await writeFile(
134
- ".pome/twin-status.json",
135
- JSON.stringify(
136
- { name, url: restUrl, rest_url: restUrl, mcp_url: mcpUrl, auth_token: token },
137
- null,
138
- 2
139
- )
140
- );
138
+ await writeStandaloneStatusFile({
139
+ name,
140
+ url: restUrl,
141
+ rest_url: restUrl,
142
+ mcp_url: mcpUrl,
143
+ auth_token: token
144
+ });
141
145
  } catch (err) {
142
146
  await new Promise((resolve) => server.close(() => resolve()));
143
147
  await harness.close();
@@ -180,4 +184,4 @@ async function runTwinStartCommand(nameArg, options) {
180
184
  process.once("SIGTERM", shutdown);
181
185
  }
182
186
 
183
- export { resolveStandaloneAuthSecret, resolveStandaloneSeed, resolveStandaloneTwin, runTwinStartCommand };
187
+ export { STANDALONE_STATUS_PATH, resolveStandaloneAuthSecret, resolveStandaloneSeed, resolveStandaloneTwin, runTwinStartCommand, writeStandaloneStatusFile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/cli",
3
- "version": "0.43.0",
3
+ "version": "0.43.1",
4
4
  "description": "Test AI agents against digital twins of real SaaS APIs. Records tool-call traces and scores them on pome.sh.",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,6 +0,0 @@
1
- export { UnsupportedTwinError, bootTwin } from './chunk-5A6HHA54.js';
2
- export { STRIPE_LOCAL_ACCOUNT_ID } from './chunk-ELIEDNF3.js';
3
- import './chunk-HRAD7MRX.js';
4
- import './chunk-5KFDRR53.js';
5
- import './chunk-SG6ZTIMT.js';
6
- import './chunk-FBSA5L36.js';