@kheleshikhi/studio-agent 0.6.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 +225 -0
- package/dist/attempt.d.ts +25 -0
- package/dist/attempt.js +197 -0
- package/dist/attempt.js.map +1 -0
- package/dist/browser.d.ts +102 -0
- package/dist/browser.js +837 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +1198 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.d.ts +17 -0
- package/dist/daemon.js +208 -0
- package/dist/daemon.js.map +1 -0
- package/dist/library.d.ts +25 -0
- package/dist/library.js +120 -0
- package/dist/library.js.map +1 -0
- package/dist/observation.d.ts +31 -0
- package/dist/observation.js +66 -0
- package/dist/observation.js.map +1 -0
- package/dist/protocol.d.ts +20 -0
- package/dist/protocol.js +99 -0
- package/dist/protocol.js.map +1 -0
- package/dist/scenario.d.ts +19 -0
- package/dist/scenario.js +90 -0
- package/dist/scenario.js.map +1 -0
- package/dist/state.d.ts +13 -0
- package/dist/state.js +111 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace.d.ts +15 -0
- package/dist/workspace.js +66 -0
- package/dist/workspace.js.map +1 -0
- package/dist/zip.d.ts +7 -0
- package/dist/zip.js +76 -0
- package/dist/zip.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# NerdBro Studio Agent
|
|
2
|
+
|
|
3
|
+
Provider-neutral external authoring for KheleShikhi Interactive Studio. It
|
|
4
|
+
requires Node 20+ and Chrome, Edge, or explicitly installed Playwright Chromium.
|
|
5
|
+
It does not require the NerdBro repository, Flutter, Dart, Python, Docker, an
|
|
6
|
+
OpenAI API key, or an Anthropic API key.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install -g @kheleshikhi/studio-agent
|
|
10
|
+
nerdbro setup
|
|
11
|
+
nerdbro connect
|
|
12
|
+
|
|
13
|
+
mkdir my-kheleshikhi-work
|
|
14
|
+
cd my-kheleshikhi-work
|
|
15
|
+
nerdbro agent init --provider codex # or claude / generic
|
|
16
|
+
nerdbro studio create --title "Ball Lab" --target combined --concept "Projectile range"
|
|
17
|
+
nerdbro studio checkout <asset-id>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The browser pairing page uses the normal NerdBro login. The daemon stores the
|
|
21
|
+
scoped production credential only in memory. It binds to `127.0.0.1` on a
|
|
22
|
+
random port and protects local RPC with a random discovery secret. The
|
|
23
|
+
persistent Playwright profile is dedicated to NerdBro and never reads personal
|
|
24
|
+
browser cookies, extensions, passwords, or history.
|
|
25
|
+
|
|
26
|
+
The normal headless Agent browser uses the real installed
|
|
27
|
+
Chrome or Edge where available, keeps the browser sandbox enabled, and leaves
|
|
28
|
+
display scaling to the operating system. Desktop and mobile viewport options
|
|
29
|
+
are CSS layout sizes; they do not simulate a low-density display. Headless mode
|
|
30
|
+
uses the same explicit responsive viewports for automated inspection, but its
|
|
31
|
+
screenshot pixels are not promised to be byte-identical across hardware.
|
|
32
|
+
Chrome's device-metrics API applies the CSS size while leaving the host device
|
|
33
|
+
scale native; NerdBro does not force a DPR value. Version 0.6.1 keeps the
|
|
34
|
+
Chrome DevTools viewport session alive for the browser tab, so a requested
|
|
35
|
+
desktop, mobile, or custom CSS size remains in effect through inspection and
|
|
36
|
+
capture.
|
|
37
|
+
`browser.json` records the actual CSS viewport, `window.devicePixelRatio`, safe
|
|
38
|
+
screen dimensions, browser version, headed/headless mode, and PNG pixel size.
|
|
39
|
+
|
|
40
|
+
## Benchmark Attempt
|
|
41
|
+
|
|
42
|
+
From a fresh Git workspace, connect once, then run:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
nerdbro attempt start --label ball-launcher-v3 --mode benchmark
|
|
46
|
+
nerdbro agent init --provider claude # or codex / generic
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The generated provider instructions tell the Agent to write `REPORT.md` and
|
|
50
|
+
`EVALUATION.md` before `nerdbro attempt finish --outcome completed|blocked|incomplete|abandoned`. The workspace pointer
|
|
51
|
+
`.nerdbro-attempt.json` holds only non-secret metadata. Commands automatically
|
|
52
|
+
join the same Attempt after a daemon restart or a new Agent authorization
|
|
53
|
+
session under the same Maker account. The client ZIP is written under
|
|
54
|
+
`~/NerdBroAttempts/<attempt-id>/client/` (override with
|
|
55
|
+
`NERDBRO_ATTEMPT_ROOT`). An operator with backend database access later runs
|
|
56
|
+
`just studio-agent-attempt-export <attempt-id>` from the repository to create
|
|
57
|
+
one outer ZIP. Ordinary Studio authoring remains available without an Attempt.
|
|
58
|
+
|
|
59
|
+
Use `--json` on every command for stable envelopes. Exit code `0` is success,
|
|
60
|
+
`1` is an operation/server failure, `2` is local input, `3` is a stale draft or
|
|
61
|
+
source conflict, `4` means authentication/daemon state is required, and `5`
|
|
62
|
+
means setup/browser compatibility failed.
|
|
63
|
+
|
|
64
|
+
The repository's `agent_capability_proof` and `ball_launcher_stress` journeys
|
|
65
|
+
are **scripted platform regressions**: repository code authors `source.json`,
|
|
66
|
+
then this public CLI drives and certifies the real browser runtime. They do not
|
|
67
|
+
show that Claude or another model independently discovered or authored a Scene.
|
|
68
|
+
In particular, the scripted Ball Launcher does not perform a vernier-caliper
|
|
69
|
+
diameter reading and choose a ball from that measurement. The original, harder
|
|
70
|
+
brief is reserved for a fresh external-AI run without repository access.
|
|
71
|
+
|
|
72
|
+
Read the current live Authoring Playbook with `nerdbro guide` before substantial
|
|
73
|
+
authoring. It is the same canonical page served at
|
|
74
|
+
`/docs/studio-language/agent-authoring-playbook`, not a bundled instruction copy.
|
|
75
|
+
|
|
76
|
+
Use `nerdbro docs`, `nerdbro docs search <query>`, and
|
|
77
|
+
`nerdbro docs open <slug>` to read the same current Language Reference hosted
|
|
78
|
+
for Makers. Schema, capabilities, Components, Behaviors, and examples are
|
|
79
|
+
always fetched live from NerdBro; none is bundled in this npm package.
|
|
80
|
+
Use `nerdbro schema experience` for the final learner content contract and
|
|
81
|
+
`nerdbro docs open experiences` for one-Scene, narrated, mixed, and multi-Band
|
|
82
|
+
examples. Use `nerdbro schema scene.variables`, `scene.events`, `scene.gestures`, `scene.diagram`,
|
|
83
|
+
`scene.motions`, `scene.captions`, `scene.audio`, `scene.stateMachines`,
|
|
84
|
+
`expressions`, `stateMachine.actions`, `mathGraph.behaviorProjection`,
|
|
85
|
+
`assessment`, or the `runtime.*` topics to request a narrow projection of
|
|
86
|
+
the parser/runtime contract. `nerdbro schema` returns the topic index and
|
|
87
|
+
summary contracts.
|
|
88
|
+
Search Components and Behaviors narrowly. Example search rejects an empty
|
|
89
|
+
query, returns ranked summaries with a small default limit, and leaves full
|
|
90
|
+
source to `example inspect <id>`; normally inspect no more than three relevant
|
|
91
|
+
examples.
|
|
92
|
+
|
|
93
|
+
The daemon reuses the authenticated context response for at most 30 seconds,
|
|
94
|
+
then revalidates it; connecting or disconnecting clears the cache. This cuts
|
|
95
|
+
repeated compatibility reads without caching mutable drafts.
|
|
96
|
+
|
|
97
|
+
Check out once, edit only `source.json`, and use `studio status` for later
|
|
98
|
+
synchronization. Validate, compile, preview when useful, save, then inspect the
|
|
99
|
+
real Studio. The daemon reuses its short-lived read-only browser credential in
|
|
100
|
+
memory and clears it on disconnect, stop, reconnect, or origin change. A graph
|
|
101
|
+
requirement without a teaching purpose or axis/synchronization meaning is a
|
|
102
|
+
clarification request, not permission to invent filler.
|
|
103
|
+
|
|
104
|
+
`studio create` needs the `STUDIO_ASSET_CREATE` pairing scope and returns a
|
|
105
|
+
Maker-owned draft identity and its server starter-source hash. `--scratch`
|
|
106
|
+
only labels the intent to use an ordinary new, unapproved asset as a disposable
|
|
107
|
+
spike. It has no separate lifecycle, automatic expiry, promotion, or garbage
|
|
108
|
+
collection. Save its exact ID for explicit cleanup after success. Use
|
|
109
|
+
`studio brief <asset>` and `studio brief update <asset> --graph-purpose ...`
|
|
110
|
+
to inspect or update authoring intent with a concurrency check.
|
|
111
|
+
|
|
112
|
+
`studio save` creates a draft. `studio certify` records technical evidence.
|
|
113
|
+
Neither approves or publishes. `studio export --revision ...` accepts only the
|
|
114
|
+
server-built canonical V2 package for an approved immutable revision; the CLI
|
|
115
|
+
does not assemble package formats, and draft export is not a command.
|
|
116
|
+
|
|
117
|
+
For visual evolution, use `nerdbro studio capture-preview <asset-id> --mode
|
|
118
|
+
semantic --viewport 480x320 --duration 8 --out preview-capture`. It captures
|
|
119
|
+
the real Studio preview into local JPEG checkpoints, a labelled contact sheet,
|
|
120
|
+
and a source/revision-bound runtime timeline. `--mode fixed --fps 2` samples
|
|
121
|
+
motion at regular intervals. The local output path is accessible to a local
|
|
122
|
+
agent only; no frames are uploaded or silently persisted by the backend.
|
|
123
|
+
|
|
124
|
+
`nerdbro studio inspect <asset-id> --view learner --viewport mobile` opens the
|
|
125
|
+
Studio's production part surface at mobile CSS size. `--view final` is an alias.
|
|
126
|
+
|
|
127
|
+
Use `nerdbro studio drive <asset-id> --scenario journey.json --viewport mobile`
|
|
128
|
+
to exercise an unsaved local source in that same production preview. This
|
|
129
|
+
does not save a draft or write learner progress. The paired browser opens the
|
|
130
|
+
current server source, transfers the bounded local candidate into Studio's
|
|
131
|
+
explicit authoring/test mode, compiles it through the normal preview API, and
|
|
132
|
+
rejects a source-hash mismatch before the first step. The scenario is versioned
|
|
133
|
+
and has at most 64 steps and a two-minute run budget:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"version": 1,
|
|
138
|
+
"steps": [
|
|
139
|
+
{"op": "set", "target": "launchSpeed", "value": 8},
|
|
140
|
+
{"op": "drag", "target": "angleGesture", "value": 42},
|
|
141
|
+
{"op": "press", "target": "launch"},
|
|
142
|
+
{"op": "wait", "path": "recent_scene_event", "equals": "projectileLanded", "timeoutMs": 10000},
|
|
143
|
+
{"op": "assert", "path": "active_states.attempt", "equals": "result"},
|
|
144
|
+
{"op": "assert", "path": "motion_outputs.projectile.range", "between": [6, 6.5]},
|
|
145
|
+
{"op": "capture", "name": "result"}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`set`, `fire`, `press`, `tap`, `drag`, `play`, `pause`, `seek`, and `reset`
|
|
151
|
+
address only declared variables, events, instances, and gestures. `press`
|
|
152
|
+
honours conditional availability; `drag` derives a Scene point from the
|
|
153
|
+
authored gesture range. `wait` and `assert` read allowlisted observation paths,
|
|
154
|
+
never arbitrary code. `capture` records the actual browser pixels. The result
|
|
155
|
+
directory contains the initial and step captures plus bounded runtime state.
|
|
156
|
+
For a gesture with an interaction handle, assert
|
|
157
|
+
`gestures.<id>.affordance_visible` before `drag`; the driver also refuses a
|
|
158
|
+
handle that the mounted preview has not presented.
|
|
159
|
+
|
|
160
|
+
## Library authoring
|
|
161
|
+
|
|
162
|
+
The Studio Library's semantic kinds — `experience`, `audio`, `component`
|
|
163
|
+
(including Characters) and `behavior` — are authored with the same
|
|
164
|
+
draft-first flow. The CLI is a thin client of `/v1/agent/authoring`: templates,
|
|
165
|
+
validation, certification profiles, preview resolution and package building all
|
|
166
|
+
run on the server, and schemas and documentation are fetched live (use
|
|
167
|
+
`nerdbro docs` and `nerdbro schema`); none is bundled here.
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
nerdbro audio upload take.m4a # prints sha256 and duration
|
|
171
|
+
nerdbro audio create narration.sunrise.v1 --name "Sunrise narration" --media <sha256> [--category c] [--language bn]
|
|
172
|
+
nerdbro experience create poem.sunrise.v1 --name "Sunrise" --kind poem \
|
|
173
|
+
--scene-asset <interactive-asset-id> # or --scene-package scene:<id> --scene <scene-id>
|
|
174
|
+
[--master-audio narration.sunrise.v1] [--duration-ms 42000]
|
|
175
|
+
nerdbro component create card.star.v1 --name "Star card" [--category c] [--visual v] [--palette p]
|
|
176
|
+
nerdbro behavior create tap.counter.v1 --name "Tap counter" [--declarative]
|
|
177
|
+
|
|
178
|
+
nerdbro <kind> checkout <stable-id> [--workspace dir] [--force]
|
|
179
|
+
nerdbro <kind> status <stable-id>
|
|
180
|
+
nerdbro <kind> save <stable-id>
|
|
181
|
+
nerdbro <kind> validate <stable-id> [--revision <revision>]
|
|
182
|
+
nerdbro <kind> certify <stable-id> [--revision <revision>]
|
|
183
|
+
nerdbro experience|behavior preview <stable-id>
|
|
184
|
+
nerdbro experience|behavior export <stable-id> --revision <revision> [--out file.kspkg]
|
|
185
|
+
nerdbro experience|audio search <query> [--limit n]
|
|
186
|
+
nerdbro experience|audio inspect <stable-id>
|
|
187
|
+
nerdbro <kind> inspect-browser <stable-id> [--viewport desktop|mobile|WxH]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`component` and `behavior` keep their catalogue `search` and `inspect`
|
|
191
|
+
commands; the authoring verbs above sit beside them.
|
|
192
|
+
|
|
193
|
+
Checkout writes `<workspace>/<kind>/<stable-id>/source.json` and
|
|
194
|
+
`library-lock.json` (`kheleshikhi.library-lock.v1`: kind, stable id, head
|
|
195
|
+
revision, canonical source hash, schema version, checkout time). The default
|
|
196
|
+
workspace is `./nerdbro-studio`. Checkout refuses to overwrite unsaved local
|
|
197
|
+
edits without `--force`. `status` reports `local_changed`, `remote_changed` and
|
|
198
|
+
`conflict`. `save` sends the whole local document with the lock's base revision;
|
|
199
|
+
saving unchanged content creates no draft but still refreshes the lock, and a
|
|
200
|
+
stale base exits `3` (`VOCABULARY_DRAFT_CONFLICT`) with `source.json` untouched.
|
|
201
|
+
`validate` and `certify` default to the lock's head revision. `preview` sends
|
|
202
|
+
the local, unsaved `source.json` and writes `preview.json` beside it.
|
|
203
|
+
`inspect-browser` captures the Studio Library workspace to `screenshot.png` and
|
|
204
|
+
`browser.json` in the checkout directory; its bounded `browser.capture` activity
|
|
205
|
+
record is reported locally in the command result only.
|
|
206
|
+
|
|
207
|
+
The flow is create or checkout → edit `source.json` → `status` → `save` (an
|
|
208
|
+
immutable draft) → `validate` → `certify`. Certification is technical evidence;
|
|
209
|
+
approval and publication are human actions in the Studio, and the CLI has no
|
|
210
|
+
approve or publish command. `export` downloads only an approved revision's
|
|
211
|
+
server-built `.kspkg`.
|
|
212
|
+
|
|
213
|
+
Audio must be AAC in an MP4 container (`.m4a` or `.mp4`) of at most 24 MiB.
|
|
214
|
+
`audio upload` checks the extension and size locally, and the server probes the
|
|
215
|
+
bytes authoritatively. The upload carries only the bytes under the neutral name
|
|
216
|
+
`upload.m4a`/`upload.mp4`; the local path and filename are never sent, printed
|
|
217
|
+
or stored. Uploading requires the `AUDIO_UPLOAD` scope, which new pairings
|
|
218
|
+
receive by default. A session paired by an older CLI gets
|
|
219
|
+
`AGENT_SCOPE_REQUIRED` (exit `4`): run `nerdbro disconnect` and then
|
|
220
|
+
`nerdbro connect`.
|
|
221
|
+
|
|
222
|
+
Screenshot SHA-256 values identify the exact local artifact inspected in the
|
|
223
|
+
activity stream; screenshot bytes, pixel size, and DPR are audit metadata, not
|
|
224
|
+
semantic certification identity. Certification remains bound to source and
|
|
225
|
+
runtime evidence rather than pixel-identical rendering.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare const POINTER_SCHEMA = "kheleshikhi.studio-agent-attempt-pointer.v1";
|
|
2
|
+
export interface AttemptPointer {
|
|
3
|
+
schema: typeof POINTER_SCHEMA;
|
|
4
|
+
attempt_id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
mode: string;
|
|
7
|
+
attempt_root: string;
|
|
8
|
+
server_origin: string;
|
|
9
|
+
started_at: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function attemptHome(): string;
|
|
12
|
+
export declare function attemptDirectory(attemptId: string): string;
|
|
13
|
+
export declare function readAttemptPointer(start?: string): Promise<{
|
|
14
|
+
pointer: AttemptPointer;
|
|
15
|
+
workspace: string;
|
|
16
|
+
} | null>;
|
|
17
|
+
export declare function createAttemptPointer(server: Record<string, unknown>, serverOrigin: string, workspace?: string): Promise<AttemptPointer>;
|
|
18
|
+
export declare function saveCommandEvidence(pointer: AttemptPointer, commandId: string, command: string, result: unknown): Promise<void>;
|
|
19
|
+
export declare function finalizeClient(pointer: AttemptPointer, workspace: string, server: Record<string, unknown>): Promise<{
|
|
20
|
+
path: string;
|
|
21
|
+
sha256: string;
|
|
22
|
+
included: number;
|
|
23
|
+
excluded: string[];
|
|
24
|
+
}>;
|
|
25
|
+
export {};
|
package/dist/attempt.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** Local Attempt pointer and privacy-bounded client evidence finalization. */
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { CliError } from "./types.js";
|
|
8
|
+
import { makeZip, sha256, verifyZip } from "./zip.js";
|
|
9
|
+
const POINTER_NAME = ".nerdbro-attempt.json";
|
|
10
|
+
const POINTER_SCHEMA = "kheleshikhi.studio-agent-attempt-pointer.v1";
|
|
11
|
+
const MAX_FILE = 32 * 1024 * 1024;
|
|
12
|
+
const MAX_TOTAL = 256 * 1024 * 1024;
|
|
13
|
+
const BLOCKED_DIRS = new Set([".git", "node_modules", ".cache", ".venv", "venv", "__pycache__", ".playwright", "browser-profile", "dist", "build"]);
|
|
14
|
+
const BLOCKED_FILES = /(^\.env(?:\..*)?$|\.pem$|\.key$|^id_rsa|^id_ed25519|cookie|credential|secret|token|\.p12$|\.pfx$|\.sqlite$)/i;
|
|
15
|
+
const SECRET_CONTENT = /nbs_[A-Za-z0-9_-]{12,}|nbp_[A-Za-z0-9_-]{12,}|Bearer\s+[A-Za-z0-9_.-]{20,}|eyJ[A-Za-z0-9_-]{30,}\.[A-Za-z0-9_-]{20,}/i;
|
|
16
|
+
export function attemptHome() {
|
|
17
|
+
return path.resolve(process.env.NERDBRO_ATTEMPT_ROOT || path.join(os.homedir(), "NerdBroAttempts"));
|
|
18
|
+
}
|
|
19
|
+
export function attemptDirectory(attemptId) {
|
|
20
|
+
if (!/^att_[A-Za-z0-9_-]{16,36}$/.test(attemptId))
|
|
21
|
+
throw new CliError("Invalid Attempt ID.", "AGENT_ATTEMPT_ID_INVALID", 2);
|
|
22
|
+
return path.join(attemptHome(), attemptId);
|
|
23
|
+
}
|
|
24
|
+
export async function readAttemptPointer(start = process.cwd()) {
|
|
25
|
+
let current = path.resolve(start);
|
|
26
|
+
while (true) {
|
|
27
|
+
try {
|
|
28
|
+
const candidate = JSON.parse(await readFile(path.join(current, POINTER_NAME), "utf8"));
|
|
29
|
+
if (candidate.schema !== POINTER_SCHEMA || !/^att_[A-Za-z0-9_-]{16,36}$/.test(candidate.attempt_id)) {
|
|
30
|
+
throw new CliError("Attempt pointer is malformed.", "AGENT_ATTEMPT_POINTER_INVALID", 2);
|
|
31
|
+
}
|
|
32
|
+
return { pointer: candidate, workspace: current };
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error instanceof CliError)
|
|
36
|
+
throw error;
|
|
37
|
+
if (error.code !== "ENOENT")
|
|
38
|
+
throw new CliError("Cannot read Attempt pointer.", "AGENT_ATTEMPT_POINTER_INVALID", 2);
|
|
39
|
+
}
|
|
40
|
+
if (current === path.dirname(current) || current === os.homedir())
|
|
41
|
+
return null;
|
|
42
|
+
current = path.dirname(current);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function createAttemptPointer(server, serverOrigin, workspace = process.cwd()) {
|
|
46
|
+
if (await readAttemptPointer(workspace))
|
|
47
|
+
throw new CliError("This workspace already has an Attempt pointer.", "AGENT_ATTEMPT_ALREADY_ACTIVE", 3);
|
|
48
|
+
const attemptId = String(server.attempt_id);
|
|
49
|
+
const root = attemptDirectory(attemptId);
|
|
50
|
+
await mkdir(attemptHome(), { recursive: true });
|
|
51
|
+
await mkdir(root, { recursive: false });
|
|
52
|
+
const pointer = {
|
|
53
|
+
schema: POINTER_SCHEMA, attempt_id: attemptId,
|
|
54
|
+
label: String(server.label), mode: String(server.mode),
|
|
55
|
+
attempt_root: root, server_origin: serverOrigin,
|
|
56
|
+
started_at: String(server.started_at),
|
|
57
|
+
};
|
|
58
|
+
await writeFile(path.join(workspace, POINTER_NAME), `${JSON.stringify(pointer, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
59
|
+
await writeFile(path.join(root, "README.md"), `# NerdBro Attempt ${attemptId}\n\nLabel: ${pointer.label}\nMode: ${pointer.mode}\n\nRun \`nerdbro attempt finish --outcome completed|blocked|incomplete|abandoned\` in the original workspace with the accurate outcome, then run \`just studio-agent-attempt-export ${attemptId}\` from the NerdBro repository.\n`);
|
|
60
|
+
await writeFile(path.join(root, "RUN_MANIFEST.json"), `${JSON.stringify({ schema: "kheleshikhi.studio-agent-attempt-run.v1", ...server }, null, 2)}\n`);
|
|
61
|
+
return pointer;
|
|
62
|
+
}
|
|
63
|
+
export async function saveCommandEvidence(pointer, commandId, command, result) {
|
|
64
|
+
const root = attemptDirectory(pointer.attempt_id);
|
|
65
|
+
const destination = path.join(root, "client", "evidence", command.startsWith("studio.drive") ? "drives" : command.startsWith("studio.inspect") ? "captures" : command.startsWith("studio.certify") ? "certification" : "other");
|
|
66
|
+
const serialized = JSON.stringify({ command_id: commandId, command, at: new Date().toISOString(), result }, null, 2);
|
|
67
|
+
if (serialized.length > 1024 * 1024)
|
|
68
|
+
return;
|
|
69
|
+
const sanitized = serialized.replace(/nbs_[A-Za-z0-9_-]+|nbp_[A-Za-z0-9_-]+|Bearer\s+[A-Za-z0-9_.-]+|eyJ[A-Za-z0-9_-]{30,}\.[A-Za-z0-9_-]+/g, "[REDACTED]");
|
|
70
|
+
await mkdir(destination, { recursive: true });
|
|
71
|
+
await writeFile(path.join(destination, `${commandId}.json`), `${sanitized}\n`, { mode: 0o600 });
|
|
72
|
+
}
|
|
73
|
+
function git(workspace, args, code = "ATTEMPT_GIT_INVALID") {
|
|
74
|
+
const result = spawnSync("git", args, { cwd: workspace, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
|
|
75
|
+
if (result.status !== 0)
|
|
76
|
+
throw new CliError(`Cannot read workspace Git state: ${result.stderr?.trim() || result.error?.message || args.join(" ")}`, code, 2);
|
|
77
|
+
return result.stdout.trim();
|
|
78
|
+
}
|
|
79
|
+
async function snapshot(workspace, relative = "", entries = [], excluded = [], total = { bytes: 0 }) {
|
|
80
|
+
for (const item of await readdir(path.join(workspace, relative), { withFileTypes: true })) {
|
|
81
|
+
const name = path.join(relative, item.name);
|
|
82
|
+
const portable = name.split(path.sep).join("/");
|
|
83
|
+
if (item.isSymbolicLink() || BLOCKED_FILES.test(item.name) || (item.isDirectory() && BLOCKED_DIRS.has(item.name))) {
|
|
84
|
+
excluded.push(portable);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (item.isDirectory()) {
|
|
88
|
+
await snapshot(workspace, name, entries, excluded, total);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!item.isFile()) {
|
|
92
|
+
excluded.push(portable);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const info = await stat(path.join(workspace, name));
|
|
96
|
+
if (info.size > MAX_FILE || total.bytes + info.size > MAX_TOTAL) {
|
|
97
|
+
excluded.push(portable);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const data = await readFile(path.join(workspace, name));
|
|
101
|
+
if (SECRET_CONTENT.test(data.toString("utf8"))) {
|
|
102
|
+
excluded.push(portable);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
entries.push({ name: `workspace/${portable}`, data });
|
|
106
|
+
total.bytes += data.length;
|
|
107
|
+
}
|
|
108
|
+
return { entries, excluded };
|
|
109
|
+
}
|
|
110
|
+
export async function finalizeClient(pointer, workspace, server) {
|
|
111
|
+
const root = attemptDirectory(pointer.attempt_id);
|
|
112
|
+
const client = path.join(root, "client");
|
|
113
|
+
const reports = path.join(client, "reports");
|
|
114
|
+
const gitDir = path.join(client, "git");
|
|
115
|
+
await mkdir(reports, { recursive: true });
|
|
116
|
+
await mkdir(gitDir, { recursive: true });
|
|
117
|
+
const reportFiles = [];
|
|
118
|
+
for (const name of ["REPORT.md", "EVALUATION.md"]) {
|
|
119
|
+
let data;
|
|
120
|
+
try {
|
|
121
|
+
data = await readFile(path.join(workspace, name));
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
if (pointer.mode === "benchmark")
|
|
125
|
+
throw new CliError(`${name} is required before finishing this benchmark Attempt.`, name === "REPORT.md" ? "ATTEMPT_REPORT_MISSING" : "ATTEMPT_EVALUATION_MISSING", 2);
|
|
126
|
+
else
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!data.length)
|
|
130
|
+
throw new CliError(`${name} is empty.`, "ATTEMPT_REPORT_EMPTY", 2);
|
|
131
|
+
if (SECRET_CONTENT.test(data.toString("utf8")))
|
|
132
|
+
throw new CliError(`${name} contains a credential-like value.`, "ATTEMPT_REPORT_SECRET", 2);
|
|
133
|
+
await writeFile(path.join(reports, name), data);
|
|
134
|
+
reportFiles.push({ name: `reports/${name}`, data });
|
|
135
|
+
}
|
|
136
|
+
const top = git(workspace, ["rev-parse", "--show-toplevel"]);
|
|
137
|
+
if (await realpath(top) !== await realpath(workspace))
|
|
138
|
+
throw new CliError("Finish from the Attempt workspace Git root.", "ATTEMPT_GIT_ROOT_REQUIRED", 2);
|
|
139
|
+
const statusText = git(workspace, ["status", "--porcelain=v1", "--untracked-files=all"]);
|
|
140
|
+
const history = `${git(workspace, ["log", "--graph", "--decorate", "--oneline", "--all"])}\n`;
|
|
141
|
+
const bundlePath = path.join(gitDir, "attempt.gitbundle");
|
|
142
|
+
git(workspace, ["bundle", "create", bundlePath, "--all"]);
|
|
143
|
+
const bundle = await readFile(bundlePath);
|
|
144
|
+
const snap = await snapshot(workspace);
|
|
145
|
+
const existing = await collectExistingEvidence(path.join(client, "evidence"));
|
|
146
|
+
const fileEntries = [
|
|
147
|
+
...reportFiles, ...snap.entries, ...existing,
|
|
148
|
+
{ name: "git/attempt.gitbundle", data: bundle },
|
|
149
|
+
{ name: "git/history.txt", data: Buffer.from(history) },
|
|
150
|
+
{ name: "git/working-tree-status.txt", data: Buffer.from(`${statusText}\n`) },
|
|
151
|
+
];
|
|
152
|
+
const manifest = {
|
|
153
|
+
schema: "kheleshikhi.studio-agent-client-manifest.v1",
|
|
154
|
+
attempt_id: pointer.attempt_id, label: pointer.label, mode: pointer.mode,
|
|
155
|
+
status: "finished", outcome: server.outcome,
|
|
156
|
+
started_at: pointer.started_at, finished_at: new Date().toISOString(),
|
|
157
|
+
cli_version: String(server.initial_cli_version || "unknown"), node_version: process.versions.node,
|
|
158
|
+
platform: `${process.platform} ${process.arch}`, server_origin: pointer.server_origin,
|
|
159
|
+
documentation_fingerprint: server.documentation_fingerprint,
|
|
160
|
+
language_contract_fingerprint: server.language_contract_fingerprint,
|
|
161
|
+
scene_schema: server.scene_schema, compiler_version: server.compiler_version,
|
|
162
|
+
runtime_fingerprint: server.runtime_fingerprint,
|
|
163
|
+
git_head: git(workspace, ["rev-parse", "HEAD"]),
|
|
164
|
+
git_status: statusText,
|
|
165
|
+
studio_asset_ids: server.studio_asset_ids || [],
|
|
166
|
+
command_counts: server.command_counts || {},
|
|
167
|
+
paths_included: fileEntries.map((entry) => entry.name),
|
|
168
|
+
paths_excluded: snap.excluded,
|
|
169
|
+
};
|
|
170
|
+
const manifestData = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
171
|
+
await writeFile(path.join(client, "CLIENT_MANIFEST.json"), manifestData);
|
|
172
|
+
fileEntries.push({ name: "CLIENT_MANIFEST.json", data: manifestData });
|
|
173
|
+
const sums = Buffer.from(fileEntries.map((entry) => `${sha256(entry.data)} ${entry.name}`).join("\n") + "\n");
|
|
174
|
+
await writeFile(path.join(client, "CLIENT_SHA256SUMS"), sums);
|
|
175
|
+
fileEntries.push({ name: "CLIENT_SHA256SUMS", data: sums });
|
|
176
|
+
const bytes = makeZip(fileEntries);
|
|
177
|
+
if (!verifyZip(bytes))
|
|
178
|
+
throw new CliError("Client ZIP failed integrity verification.", "ATTEMPT_CLIENT_ZIP_INVALID");
|
|
179
|
+
const zipPath = path.join(client, "client-attempt.zip");
|
|
180
|
+
const temporary = `${zipPath}.${randomBytes(4).toString("hex")}.tmp`;
|
|
181
|
+
await writeFile(temporary, bytes);
|
|
182
|
+
await (await import("node:fs/promises")).rename(temporary, zipPath);
|
|
183
|
+
const digest = sha256(bytes);
|
|
184
|
+
await writeFile(path.join(client, "CLIENT_COMPLETE"), `${JSON.stringify({ attempt_id: pointer.attempt_id, zip_sha256: digest, finished_at: manifest.finished_at })}\n`);
|
|
185
|
+
return { path: zipPath, sha256: digest, included: fileEntries.length, excluded: snap.excluded };
|
|
186
|
+
}
|
|
187
|
+
async function collectExistingEvidence(directory) {
|
|
188
|
+
try {
|
|
189
|
+
await lstat(directory);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const found = await snapshot(directory);
|
|
195
|
+
return found.entries.map((entry) => ({ name: `evidence/${entry.name.slice("workspace/".length)}`, data: entry.data }));
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=attempt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.js","sourceRoot":"","sources":["../src/attempt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type Page } from "playwright";
|
|
2
|
+
import { AgentApi } from "./protocol.js";
|
|
3
|
+
import { type DriveScenario } from "./scenario.js";
|
|
4
|
+
export declare function studioBrowserLaunchOptions(headless: boolean): {
|
|
5
|
+
readonly headless: boolean;
|
|
6
|
+
readonly viewport: null;
|
|
7
|
+
readonly chromiumSandbox: true;
|
|
8
|
+
};
|
|
9
|
+
export declare function browserProbeLaunchOptions(channel?: "chrome" | "msedge"): {
|
|
10
|
+
readonly channel: "chrome" | "msedge" | undefined;
|
|
11
|
+
readonly headless: true;
|
|
12
|
+
readonly chromiumSandbox: true;
|
|
13
|
+
};
|
|
14
|
+
export declare function cssViewport(value: string): {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function waitForCapturePaint(page: Page): Promise<void>;
|
|
19
|
+
export declare function applyCssViewport(page: Page, dimensions: {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
export declare function renderingDiagnostics(page: Page, screenshot: Buffer): Promise<Record<string, unknown>>;
|
|
24
|
+
export declare const VIEW_QUERY: Record<string, Record<string, string>>;
|
|
25
|
+
export declare function viewRequiresRuntimeObservation(view: string): boolean;
|
|
26
|
+
interface BrowserCredential {
|
|
27
|
+
token: string;
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
webOrigin: string;
|
|
30
|
+
}
|
|
31
|
+
interface BrowserAuthApi {
|
|
32
|
+
readonly origin: string;
|
|
33
|
+
request<T>(method: string, route: string, body?: unknown, authenticated?: boolean): Promise<T>;
|
|
34
|
+
}
|
|
35
|
+
/** Memory-only reuse of the short-lived browser JWT; bootstrap tickets stay one-use. */
|
|
36
|
+
export declare class BrowserCredentialManager {
|
|
37
|
+
private api;
|
|
38
|
+
private now;
|
|
39
|
+
private credential?;
|
|
40
|
+
private bootstrapCount;
|
|
41
|
+
private readonly apiOrigin;
|
|
42
|
+
constructor(api: BrowserAuthApi, now?: () => number);
|
|
43
|
+
clear(): void;
|
|
44
|
+
diagnostics(): {
|
|
45
|
+
browser_credential_cached: boolean;
|
|
46
|
+
browser_bootstrap_count: number;
|
|
47
|
+
};
|
|
48
|
+
tokenForRequest(requestUrl: string): string | undefined;
|
|
49
|
+
get(forceRefresh?: boolean): Promise<BrowserCredential>;
|
|
50
|
+
}
|
|
51
|
+
export interface BrowserCaptureRequest {
|
|
52
|
+
asset: string;
|
|
53
|
+
sourceHash: string;
|
|
54
|
+
view?: string;
|
|
55
|
+
viewport?: string;
|
|
56
|
+
fullPage?: boolean;
|
|
57
|
+
out: string;
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
observation?: boolean;
|
|
60
|
+
drive?: boolean;
|
|
61
|
+
/** Library workspace route (`?workspace=<name>&selection=<stable id>`) instead of a Scene asset. */
|
|
62
|
+
library?: {
|
|
63
|
+
workspace: string;
|
|
64
|
+
selection: string;
|
|
65
|
+
revision?: string;
|
|
66
|
+
definitionHash?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface PreviewObservationRequest extends BrowserCaptureRequest {
|
|
70
|
+
mode?: string;
|
|
71
|
+
duration?: number;
|
|
72
|
+
fps?: number;
|
|
73
|
+
}
|
|
74
|
+
export declare class StudioBrowser {
|
|
75
|
+
private api;
|
|
76
|
+
private context?;
|
|
77
|
+
private page?;
|
|
78
|
+
private headless;
|
|
79
|
+
private browserEngine;
|
|
80
|
+
private apiOrigin;
|
|
81
|
+
private credentials;
|
|
82
|
+
constructor(api: AgentApi);
|
|
83
|
+
start(headless: boolean): Promise<void>;
|
|
84
|
+
stop(): Promise<void>;
|
|
85
|
+
clearAuthentication(): void;
|
|
86
|
+
authenticationDiagnostics(): {
|
|
87
|
+
browser_credential_cached: boolean;
|
|
88
|
+
browser_bootstrap_count: number;
|
|
89
|
+
browser_engine: string;
|
|
90
|
+
};
|
|
91
|
+
private authenticatedPage;
|
|
92
|
+
open(asset: string, view?: string, allowRefresh?: boolean): Promise<Record<string, unknown>>;
|
|
93
|
+
capture(request: BrowserCaptureRequest, allowRefresh?: boolean): Promise<Record<string, unknown>>;
|
|
94
|
+
drive(request: BrowserCaptureRequest & {
|
|
95
|
+
scenario: DriveScenario;
|
|
96
|
+
candidate: Record<string, unknown>;
|
|
97
|
+
candidateHash: string;
|
|
98
|
+
}): Promise<Record<string, unknown>>;
|
|
99
|
+
/** Local-agent capture from the same browser page and renderer the Maker sees. */
|
|
100
|
+
capturePreview(request: PreviewObservationRequest): Promise<Record<string, unknown>>;
|
|
101
|
+
}
|
|
102
|
+
export {};
|