tardie 0.1.0 → 0.2.0-rc
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 +24 -5
- package/examples/quickstart/actor.ts +49 -0
- package/package.json +3 -2
- package/src/cli/build.ts +18 -4
- package/src/cli/commands.ts +41 -3
- package/src/cli/init.ts +65 -0
- package/src/cli/push.ts +16 -3
- package/src/cli/template.ts +62 -0
- package/src/cli/workflow.ts +23 -0
- package/ui/assets/index-DUtQpFbK.css +1 -0
- package/ui/assets/index-PaohSk2M.js +21 -0
- package/ui/index.html +2 -2
- package/ui/assets/index-BCrEEGCU.css +0 -1
- package/ui/assets/index-D12zyLjs.js +0 -21
package/README.md
CHANGED
|
@@ -29,27 +29,46 @@ $$\lbrace\mathrm{transitions}\rbrace = f(\mathrm{log})$$
|
|
|
29
29
|
|
|
30
30
|
## Quickstart
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
### For agents
|
|
33
|
+
|
|
34
|
+
Copy this prompt into your coding agent, or install the [Tardigrade skill](skills/tardigrade/SKILL.md):
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
Use https://github.com/clavia-labs/tardigrade and follow skills/tardigrade/SKILL.md to create, author, build, push, and run a local actor. Share its Voyager trace URL.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### For developers
|
|
41
|
+
|
|
42
|
+
Install Tardigrade and initialize an editable template actor. Use Bun 1.4 or later.
|
|
33
43
|
|
|
34
44
|
```bash
|
|
35
45
|
bun add -g tardie
|
|
46
|
+
tdg init researcher
|
|
47
|
+
cd researcher
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The `init` command creates `researcher/actor.ts` from the bundled quickstart template. Edit it to describe the agent, then build and push it into the local actor registry:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
tdg build actor.ts
|
|
54
|
+
tdg push actor.ts --target local
|
|
36
55
|
tdg dev
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
Start
|
|
58
|
+
Keep `tdg dev` running. Start the actor from another shell in the same directory:
|
|
40
59
|
|
|
41
60
|
```bash
|
|
42
|
-
tdg run "read this repo and tell me what it does"
|
|
61
|
+
tdg run "read this repo and tell me what it does" --actor researcher
|
|
43
62
|
```
|
|
44
63
|
|
|
45
|
-
|
|
64
|
+
Voyager opens at [localhost:4242](http://localhost:4242) by default. `tdg run` prints the direct Voyager URL for the new trace.
|
|
46
65
|
|
|
47
66
|
<picture>
|
|
48
67
|
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/voyager-dark.png">
|
|
49
68
|
<img alt="The voyager: a thread's log, one row per event" src="docs/assets/voyager-light.png">
|
|
50
69
|
</picture>
|
|
51
70
|
|
|
52
|
-
## Build
|
|
71
|
+
## Build your own harness
|
|
53
72
|
|
|
54
73
|
```bash
|
|
55
74
|
bun add tardie
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
agentOf,
|
|
3
|
+
agentsPackage,
|
|
4
|
+
budget,
|
|
5
|
+
codeModeFor,
|
|
6
|
+
compaction,
|
|
7
|
+
defineActor,
|
|
8
|
+
fetchPackage,
|
|
9
|
+
filesPackage,
|
|
10
|
+
reply,
|
|
11
|
+
workspacePackage
|
|
12
|
+
} from "tardie"
|
|
13
|
+
|
|
14
|
+
// actorName is the stable name used by build, push, and run.
|
|
15
|
+
const actorName = "researcher"
|
|
16
|
+
|
|
17
|
+
// actorInstructions is the main place to describe the job and its expected answer.
|
|
18
|
+
const actorInstructions = `
|
|
19
|
+
You are ${actorName}, a focused research agent.
|
|
20
|
+
|
|
21
|
+
Investigate the user's request carefully.
|
|
22
|
+
Use project files as evidence.
|
|
23
|
+
Delegate independent research when it helps.
|
|
24
|
+
Return a concise answer with concrete findings.
|
|
25
|
+
`.trim()
|
|
26
|
+
|
|
27
|
+
const instructions = {
|
|
28
|
+
name: "instructions",
|
|
29
|
+
system: actorInstructions
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default defineActor({
|
|
33
|
+
name: actorName,
|
|
34
|
+
// agentOf combines small capabilities into the actor's behavior.
|
|
35
|
+
actor: agentOf([
|
|
36
|
+
instructions,
|
|
37
|
+
// codeModeFor gives the model one code tool over the packages listed here.
|
|
38
|
+
codeModeFor({
|
|
39
|
+
// packages grant access to local files, HTTP, child agents, and saved tool results.
|
|
40
|
+
packages: [filesPackage(), fetchPackage(), agentsPackage(), workspacePackage()]
|
|
41
|
+
}),
|
|
42
|
+
// reply returns a finished turn to the actor that delegated it.
|
|
43
|
+
reply,
|
|
44
|
+
// budget stops work tools when the turn reaches its tool-call limit.
|
|
45
|
+
budget,
|
|
46
|
+
// compaction summarizes older context when a long turn outgrows its context window.
|
|
47
|
+
compaction
|
|
48
|
+
])
|
|
49
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tardie",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-rc",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Clavia, Inc.",
|
|
6
6
|
"description": "A durable agent harness. State is a pure function of the log.",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"src",
|
|
20
|
-
"ui"
|
|
20
|
+
"ui",
|
|
21
|
+
"examples"
|
|
21
22
|
],
|
|
22
23
|
"engines": {
|
|
23
24
|
"bun": ">=1.4.0"
|
package/src/cli/build.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto"
|
|
2
2
|
import { mkdtemp, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"
|
|
3
3
|
import { dirname, join, resolve } from "node:path"
|
|
4
|
-
import { pathToFileURL } from "node:url"
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
ACTOR_ARTIFACT_VERSION,
|
|
@@ -10,9 +10,12 @@ import {
|
|
|
10
10
|
type ActorDefinition
|
|
11
11
|
} from "tardie"
|
|
12
12
|
|
|
13
|
+
import { shellWord } from "./workflow"
|
|
14
|
+
|
|
13
15
|
export const DEFAULT_BUILD_DIRECTORY = ".tardigrade/build"
|
|
14
16
|
export const ACTOR_MODULE_FILE = "actor.mjs"
|
|
15
17
|
export const ACTOR_MANIFEST_FILE = "manifest.json"
|
|
18
|
+
export const TARDIE_ENTRY = fileURLToPath(import.meta.resolve("tardie"))
|
|
16
19
|
|
|
17
20
|
export interface BuildActorOptions {
|
|
18
21
|
readonly out?: string
|
|
@@ -45,6 +48,13 @@ const definitionOf = async (modulePath: string): Promise<ActorDefinition<unknown
|
|
|
45
48
|
return candidate as ActorDefinition<unknown>
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
export const tardiePlugin = (entry: string = TARDIE_ENTRY): Bun.BunPlugin => ({
|
|
52
|
+
name: "tardie",
|
|
53
|
+
setup(builder) {
|
|
54
|
+
builder.onResolve({ filter: /^tardie$/ }, () => ({ path: entry }))
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
48
58
|
export const buildActor = async (entry: string, options: BuildActorOptions = {}): Promise<BuiltActor> => {
|
|
49
59
|
const cwd = resolve(options.cwd ?? process.cwd())
|
|
50
60
|
const source = resolve(cwd, entry)
|
|
@@ -59,7 +69,8 @@ export const buildActor = async (entry: string, options: BuildActorOptions = {})
|
|
|
59
69
|
target: "bun",
|
|
60
70
|
format: "esm",
|
|
61
71
|
minify: false,
|
|
62
|
-
sourcemap: "none"
|
|
72
|
+
sourcemap: "none",
|
|
73
|
+
plugins: [tardiePlugin()]
|
|
63
74
|
})
|
|
64
75
|
if (!result.success) {
|
|
65
76
|
const detail = result.logs.map((log) => log.message).join("\n")
|
|
@@ -99,9 +110,12 @@ export const buildActor = async (entry: string, options: BuildActorOptions = {})
|
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
export const buildSummary = (built: BuiltActor): string =>
|
|
113
|
+
export const buildSummary = (built: BuiltActor, entry: string): string =>
|
|
103
114
|
[
|
|
104
115
|
`built ${built.manifest.name}`,
|
|
105
116
|
`at ${built.directory}`,
|
|
106
|
-
`hash ${built.manifest.digest}
|
|
117
|
+
`hash ${built.manifest.digest}`,
|
|
118
|
+
"",
|
|
119
|
+
"next",
|
|
120
|
+
` tdg push ${shellWord(entry)} --target local`
|
|
107
121
|
].join("\n")
|
package/src/cli/commands.ts
CHANGED
|
@@ -7,10 +7,12 @@ import { modelIsConfigured } from "tardie/server/host"
|
|
|
7
7
|
import { buildActor, buildSummary, DEFAULT_BUILD_DIRECTORY } from "./build"
|
|
8
8
|
import { readFileConfig, resolveRemote, resolveServer } from "./config"
|
|
9
9
|
import { availableDevPort, DEFAULT_MIN_PORT, DEV_URL_HOST, dev, openBrowser } from "./dev"
|
|
10
|
+
import { initActor, initSummary } from "./init"
|
|
10
11
|
import { DEFAULT_ACTOR_DIRECTORY, pushActor, pushSummary, PUSH_TARGETS } from "./push"
|
|
11
12
|
import { homeOf, HOME_MISSING, setupJson, setupPrompt, setupSummary, writeSetup } from "./setup"
|
|
12
13
|
import { actorsTable, threadsTable, DEFAULT_DETAIL_WIDTH, eventsTable, jsonOf, turnLines } from "./render"
|
|
13
14
|
import { Cli, type CliProjections } from "./services"
|
|
15
|
+
import { traceUrlFor } from "./workflow"
|
|
14
16
|
|
|
15
17
|
// The command tree. Every command is a declaration: its flags, its arguments, and its description
|
|
16
18
|
// are values, so the help a person reads and the completions a shell installs are generated from
|
|
@@ -159,6 +161,36 @@ export const setupCommand = Command.make("setup", { json }, (flags) =>
|
|
|
159
161
|
])
|
|
160
162
|
)
|
|
161
163
|
|
|
164
|
+
export const initCommand = Command.make("init", {
|
|
165
|
+
name: Argument.string("name").pipe(Argument.withDescription("The actor name")),
|
|
166
|
+
dir: Flag.string("dir").pipe(
|
|
167
|
+
Flag.withDescription("The directory to create. Defaults to a directory named after the actor."),
|
|
168
|
+
Flag.optional
|
|
169
|
+
),
|
|
170
|
+
force: Flag.boolean("force").pipe(
|
|
171
|
+
Flag.withDescription("Replace actor.ts when it already exists."),
|
|
172
|
+
Flag.withDefault(false)
|
|
173
|
+
),
|
|
174
|
+
json
|
|
175
|
+
}, (flags) =>
|
|
176
|
+
Effect.gen(function*() {
|
|
177
|
+
const directory = stated(flags.dir)
|
|
178
|
+
const initialized = yield* Effect.tryPromise({
|
|
179
|
+
try: () => initActor(flags.name, {
|
|
180
|
+
...(directory === undefined ? {} : { directory }),
|
|
181
|
+
force: flags.force
|
|
182
|
+
}),
|
|
183
|
+
catch: userErrorOf
|
|
184
|
+
})
|
|
185
|
+
yield* Console.log(flags.json ? jsonOf(initialized) : initSummary(initialized))
|
|
186
|
+
})).pipe(
|
|
187
|
+
Command.withDescription("Create an editable actor from the bundled quickstart."),
|
|
188
|
+
Command.withExamples([
|
|
189
|
+
{ command: "tdg init researcher", description: "Create researcher/actor.ts and print the local workflow" },
|
|
190
|
+
{ command: "tdg init reviewer --dir actors/reviewer", description: "Create the actor in a stated directory" }
|
|
191
|
+
])
|
|
192
|
+
)
|
|
193
|
+
|
|
162
194
|
export const buildCommand = Command.make("build", {
|
|
163
195
|
entry: Argument.string("entry").pipe(Argument.withDescription("The actor source file to bundle")),
|
|
164
196
|
out: Flag.string("out").pipe(
|
|
@@ -173,7 +205,7 @@ export const buildCommand = Command.make("build", {
|
|
|
173
205
|
try: () => buildActor(flags.entry, out === undefined ? {} : { out }),
|
|
174
206
|
catch: userErrorOf
|
|
175
207
|
})
|
|
176
|
-
yield* Console.log(flags.json ? jsonOf(built) : buildSummary(built))
|
|
208
|
+
yield* Console.log(flags.json ? jsonOf(built) : buildSummary(built, flags.entry))
|
|
177
209
|
})).pipe(
|
|
178
210
|
Command.withDescription("Bundle and validate one named actor as a portable artifact."),
|
|
179
211
|
Command.withExamples([
|
|
@@ -346,7 +378,13 @@ export const runCommand = Command.make("run", {
|
|
|
346
378
|
const id = stated(flags.id) ?? cli.mintId()
|
|
347
379
|
const accepted = yield* call(() => client.append(thread, { type: "MessageReceived", id, text: flags.brief }))
|
|
348
380
|
const view = yield* settle(client, accepted.thread, id, flags.poll, flags.timeout)
|
|
349
|
-
yield* Console.log(
|
|
381
|
+
yield* Console.log(
|
|
382
|
+
flags.json
|
|
383
|
+
? jsonOf(view)
|
|
384
|
+
: view.status === SETTLED
|
|
385
|
+
? `${turnLines(accepted.thread, view)}\n\ntrace\n ${traceUrlFor(client.baseUrl, client.actor, accepted.thread)}`
|
|
386
|
+
: turnLines(accepted.thread, view)
|
|
387
|
+
)
|
|
350
388
|
if (view.status !== SETTLED) {
|
|
351
389
|
return yield* Effect.fail(userErrorOf(`turn ${view.turn} on thread ${accepted.thread} is ${view.status}`))
|
|
352
390
|
}
|
|
@@ -444,5 +482,5 @@ export const tdg = Command.make("tdg").pipe(
|
|
|
444
482
|
Command.withDescription(
|
|
445
483
|
"The tardigrade command. Every read is a projection of a durable log, and every failure is the server's own problem document."
|
|
446
484
|
),
|
|
447
|
-
Command.withSubcommands([setupCommand, buildCommand, pushCommand, devCommand, actorsCommand, runCommand, sendCommand, lsCommand, eventsCommand])
|
|
485
|
+
Command.withSubcommands([setupCommand, initCommand, buildCommand, pushCommand, devCommand, actorsCommand, runCommand, sendCommand, lsCommand, eventsCommand])
|
|
448
486
|
)
|
package/src/cli/init.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises"
|
|
2
|
+
import { dirname, relative, resolve } from "node:path"
|
|
3
|
+
|
|
4
|
+
import { actorTemplate } from "./template"
|
|
5
|
+
import { runCommandFor, shellWord } from "./workflow"
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_ACTOR_ENTRY = "actor.ts"
|
|
8
|
+
|
|
9
|
+
export interface InitActorOptions {
|
|
10
|
+
readonly cwd?: string
|
|
11
|
+
readonly directory?: string
|
|
12
|
+
readonly force?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface InitializedActor {
|
|
16
|
+
readonly name: string
|
|
17
|
+
readonly directory: string
|
|
18
|
+
readonly entry: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const defaultInitDirectory = (name: string): string => name
|
|
22
|
+
|
|
23
|
+
const existsError = (error: unknown): boolean =>
|
|
24
|
+
typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST"
|
|
25
|
+
|
|
26
|
+
export const initActor = async (name: string, options: InitActorOptions = {}): Promise<InitializedActor> => {
|
|
27
|
+
const cwd = options.cwd ?? process.cwd()
|
|
28
|
+
const directory = resolve(cwd, options.directory ?? defaultInitDirectory(name))
|
|
29
|
+
const entry = resolve(directory, DEFAULT_ACTOR_ENTRY)
|
|
30
|
+
const source = await actorTemplate({ name })
|
|
31
|
+
|
|
32
|
+
await mkdir(dirname(entry), { recursive: true })
|
|
33
|
+
try {
|
|
34
|
+
await writeFile(entry, source, { encoding: "utf8", flag: options.force === true ? "w" : "wx" })
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (existsError(error)) {
|
|
37
|
+
throw new Error(`actor already exists at ${entry}. Choose another directory or pass --force.`)
|
|
38
|
+
}
|
|
39
|
+
throw error
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { name, directory, entry }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const shownPath = (cwd: string, path: string): string => {
|
|
46
|
+
const shown = relative(cwd, path)
|
|
47
|
+
return shown.length === 0 ? "." : shown
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const initSummary = (actor: InitializedActor, cwd: string = process.cwd()): string => {
|
|
51
|
+
const directory = shownPath(cwd, actor.directory)
|
|
52
|
+
const entry = shownPath(actor.directory, actor.entry)
|
|
53
|
+
return [
|
|
54
|
+
`created ${shownPath(cwd, actor.entry)}`,
|
|
55
|
+
"",
|
|
56
|
+
"next",
|
|
57
|
+
` cd ${shellWord(directory)}`,
|
|
58
|
+
` tdg build ${shellWord(entry)}`,
|
|
59
|
+
` tdg push ${shellWord(entry)} --target local`,
|
|
60
|
+
" tdg dev",
|
|
61
|
+
"",
|
|
62
|
+
"then, in another terminal",
|
|
63
|
+
` ${runCommandFor(actor.name)}`
|
|
64
|
+
].join("\n")
|
|
65
|
+
}
|
package/src/cli/push.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { join, resolve } from "node:path"
|
|
|
5
5
|
import type { ActorArtifactManifest } from "tardie"
|
|
6
6
|
|
|
7
7
|
import { ACTOR_MANIFEST_FILE, ACTOR_MODULE_FILE, buildActor, type BuildActorOptions, type BuiltActor } from "./build"
|
|
8
|
+
import { runCommandFor } from "./workflow"
|
|
8
9
|
|
|
9
10
|
export const DEFAULT_ACTOR_DIRECTORY = ".tardigrade/actors"
|
|
10
11
|
export const PUSH_PATH = "/v1/actors"
|
|
@@ -98,10 +99,22 @@ export const pushActor = async (entry: string, options: PushActorOptions): Promi
|
|
|
98
99
|
return options.target === "local" ? pushLocal(built, options) : pushHosted(built, options)
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
export const pushSummary = (pushed: PushedActor): string =>
|
|
102
|
-
[
|
|
102
|
+
export const pushSummary = (pushed: PushedActor): string => {
|
|
103
|
+
const summary = [
|
|
103
104
|
`pushed ${pushed.manifest.name}`,
|
|
104
105
|
`to ${pushed.target}`,
|
|
105
106
|
`at ${pushed.location}`,
|
|
106
107
|
`hash ${pushed.manifest.digest}`
|
|
107
|
-
]
|
|
108
|
+
]
|
|
109
|
+
if (pushed.target === "local") {
|
|
110
|
+
summary.push(
|
|
111
|
+
"",
|
|
112
|
+
"next",
|
|
113
|
+
" tdg dev",
|
|
114
|
+
"",
|
|
115
|
+
"then, in another terminal",
|
|
116
|
+
` ${runCommandFor(pushed.manifest.name)}`
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
return summary.join("\n")
|
|
120
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ACTOR_NAME_PATTERN } from "tardie"
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import { readFile } from "node:fs/promises"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
|
|
6
|
+
export interface ActorTemplateOptions {
|
|
7
|
+
readonly name: string
|
|
8
|
+
readonly instructions?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const INSTALLED_ACTOR_TEMPLATE = "../../examples/quickstart/actor.ts"
|
|
12
|
+
export const REPO_ACTOR_TEMPLATE = "../../../examples/quickstart/actor.ts"
|
|
13
|
+
export const ACTOR_TEMPLATE_CANDIDATES: ReadonlyArray<string> = [INSTALLED_ACTOR_TEMPLATE, REPO_ACTOR_TEMPLATE]
|
|
14
|
+
|
|
15
|
+
const templateLiteralOf = (value: string): string =>
|
|
16
|
+
value
|
|
17
|
+
.replaceAll("\\", "\\\\")
|
|
18
|
+
.replaceAll("`", "\\`")
|
|
19
|
+
.replaceAll("${", "\\${")
|
|
20
|
+
|
|
21
|
+
const replaceExactlyOnce = (source: string, pattern: RegExp, replacement: string, field: string): string => {
|
|
22
|
+
const matches = source.match(pattern)
|
|
23
|
+
if (matches === null || matches.length !== 1) throw new Error(`quickstart template must contain one ${field}`)
|
|
24
|
+
return source.replace(pattern, replacement)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const renderActorTemplate = (source: string, options: ActorTemplateOptions): string => {
|
|
28
|
+
if (!ACTOR_NAME_PATTERN.test(options.name)) {
|
|
29
|
+
throw new Error(`actor name must match ${String(ACTOR_NAME_PATTERN)}, got ${JSON.stringify(options.name)}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let rendered = replaceExactlyOnce(
|
|
33
|
+
source,
|
|
34
|
+
/^const actorName = .+$/gmu,
|
|
35
|
+
`const actorName = ${JSON.stringify(options.name)}`,
|
|
36
|
+
"actorName declaration"
|
|
37
|
+
)
|
|
38
|
+
if (options.instructions === undefined) return rendered
|
|
39
|
+
|
|
40
|
+
const instructions = options.instructions.trim()
|
|
41
|
+
if (instructions.length === 0) throw new Error("actor instructions must not be blank")
|
|
42
|
+
rendered = replaceExactlyOnce(
|
|
43
|
+
rendered,
|
|
44
|
+
/^const actorInstructions = `[\s\S]*?`\.trim\(\)$/gmu,
|
|
45
|
+
`const actorInstructions = \`\n${templateLiteralOf(instructions)}\n\`.trim()`,
|
|
46
|
+
"actorInstructions declaration"
|
|
47
|
+
)
|
|
48
|
+
return rendered
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const loadActorTemplate = async (candidates: ReadonlyArray<string>): Promise<string> => {
|
|
52
|
+
const found = candidates.find((candidate) => existsSync(candidate))
|
|
53
|
+
if (found === undefined) throw new Error(`quickstart template is missing. Looked in: ${candidates.join(", ")}`)
|
|
54
|
+
return readFile(found, "utf8")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const actorTemplate = async (
|
|
58
|
+
options: ActorTemplateOptions,
|
|
59
|
+
candidates: ReadonlyArray<string> = ACTOR_TEMPLATE_CANDIDATES.map((candidate) =>
|
|
60
|
+
fileURLToPath(new URL(candidate, import.meta.url))
|
|
61
|
+
)
|
|
62
|
+
): Promise<string> => renderActorTemplate(await loadActorTemplate(candidates), options)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// DEFAULT_ONBOARDING_BRIEF is the first local run suggested after an actor is pushed.
|
|
2
|
+
export const DEFAULT_ONBOARDING_BRIEF = "Read this repository and tell me what it does"
|
|
3
|
+
|
|
4
|
+
// shellWord quotes a generated shell argument when spaces or shell punctuation make a bare word unsafe (workflow.test.ts).
|
|
5
|
+
export const shellWord = (value: string): string =>
|
|
6
|
+
/^[A-Za-z0-9_./-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`
|
|
7
|
+
|
|
8
|
+
// runCommandFor renders the local quickstart command with its actor and brief stated.
|
|
9
|
+
export const runCommandFor = (
|
|
10
|
+
actor: string,
|
|
11
|
+
brief: string = DEFAULT_ONBOARDING_BRIEF
|
|
12
|
+
): string => `tdg run ${shellWord(brief)} --actor ${shellWord(actor)}`
|
|
13
|
+
|
|
14
|
+
// traceUrlFor selects the actor and thread in the Voyager served at the API origin (apps/voyager/src/nav.ts, Route).
|
|
15
|
+
export const traceUrlFor = (baseUrl: string, actor: string, thread: string): string => {
|
|
16
|
+
const url = new URL(baseUrl)
|
|
17
|
+
url.pathname = "/"
|
|
18
|
+
url.search = ""
|
|
19
|
+
url.hash = ""
|
|
20
|
+
url.searchParams.set("actor", actor)
|
|
21
|
+
url.searchParams.set("thread", thread)
|
|
22
|
+
return url.toString()
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg:#fafaf9;--surface:#fff;--sunken:#f2f2f0;--hair:#e7e7e4;--ink:#1a1c1b;--ink-2:#565a57;--faint:#9a9e9a;--neutral:#59605b;--neutral-wash:#eceeeb;--moss:#4f7a5c;--moss-ink:#3d6249;--moss-wash:#eef3ef;--run:#a07617;--run-wash:#f7f0dd;--ok:#567a5c;--ok-wash:#edf2ee;--wait:#5b6ea8;--wait-wash:#eceff7;--fail:#b3452e;--fail-wash:#f9ece8;--shadow:0 1px 2px #1a1c1b0f, 0 8px 24px #1a1c1b14;--sans:"IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;--mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--text-meta:12px;--text-dense:13px;--text-read:15px;--text-pane:18px;--text-page:22px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:24px;--space-6:32px;--space-7:48px;--row-h:36px;--gutter:8px;--measure:65ch;--radius:6px;--radius-chip:999px;--ease:cubic-bezier(.23, 1, .32, 1);--fast:.12s;--base:.18s;--breath:2.4s;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--bg:#131514;--surface:#1b1d1c;--sunken:#0e100f;--hair:#2a2d2b;--ink:#e8eae8;--ink-2:#a3a8a4;--faint:#6b706c;--neutral:#b8bdb9;--neutral-wash:#272a28;--moss:#7fae8c;--moss-ink:#93c2a0;--moss-wash:#22302a;--run:#d0a83e;--run-wash:#2e2a1c;--ok:#85ad8d;--ok-wash:#1f2a23;--wait:#8ea0d6;--wait-wash:#232838;--fail:#d97757;--fail-wash:#33231e;--shadow:0 1px 2px #0006, 0 8px 24px #0000007a;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}:root[data-theme=dark]{--bg:#131514;--surface:#1b1d1c;--sunken:#0e100f;--hair:#2a2d2b;--ink:#e8eae8;--ink-2:#a3a8a4;--faint:#6b706c;--neutral:#b8bdb9;--neutral-wash:#272a28;--moss:#7fae8c;--moss-ink:#93c2a0;--moss-wash:#22302a;--run:#d0a83e;--run-wash:#2e2a1c;--ok:#85ad8d;--ok-wash:#1f2a23;--wait:#8ea0d6;--wait-wash:#232838;--fail:#d97757;--fail-wash:#33231e;--shadow:0 1px 2px #0006, 0 8px 24px #0000007a;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*,:before,:after{box-sizing:border-box}html,body,#root{height:100%}body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:var(--text-dense);-webkit-font-smoothing:antialiased;margin:0}a{color:var(--moss);text-decoration:none}a:hover{color:var(--moss-ink)}button,input,textarea{font:inherit;color:inherit}:focus-visible{outline:2px solid var(--moss);outline-offset:2px}.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}@keyframes breathe{0%,to{opacity:1}50%{opacity:.55}}.breathe{animation:breathe var(--breath) ease-in-out infinite}@media (prefers-reduced-motion:reduce){.breathe{opacity:1;animation:none}*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.chip{border-radius:var(--radius-chip);height:18px;font-family:var(--mono);white-space:nowrap;align-items:center;gap:5px;padding:0 7px;font-size:10.5px;display:inline-flex}.chip-running{background:var(--run-wash);color:var(--run)}.chip-settled{background:var(--ok-wash);color:var(--ok)}.chip-blocked{background:var(--wait-wash);color:var(--wait)}.chip-failed{background:var(--fail-wash);color:var(--fail)}.input{border:1px solid var(--hair);border-radius:var(--radius);background:var(--bg);min-width:0;height:32px;font-size:var(--text-meta);outline:none;padding:0 10px}.input:focus{border-color:var(--moss)}.problem{padding:var(--space-3) var(--space-4);border-radius:var(--radius);background:var(--fail-wash);color:var(--fail)}.problem-title{font-weight:600}.problem-detail{margin-top:var(--space-1);font-size:var(--text-meta);color:var(--ink-2)}.reading{font-size:var(--text-read);max-width:var(--measure);line-height:1.6}.rail{background:var(--surface);border-right:1px solid var(--hair);min-height:0;transition:width var(--base) var(--ease);flex-direction:column;flex-shrink:0;display:flex}.rail-head{height:100%;padding:var(--space-3) var(--space-4);justify-content:space-between;align-items:flex-end;display:flex}.rail-wordmark{letter-spacing:-.01em;white-space:nowrap;align-items:baseline;gap:5px;font-size:14px;font-weight:600;display:flex}.rail-product-context{color:var(--faint);letter-spacing:0;font-size:10px;font-weight:400}.rail-section-title,.actor-label{color:var(--faint);letter-spacing:.07em;text-transform:uppercase;font-size:10.5px}.actor-rail{border-right:1px solid var(--hair);background:var(--bg);flex-direction:column;flex-shrink:0;min-height:0;display:flex}.pane-chrome{border-bottom:1px solid var(--hair);flex-shrink:0;overflow:hidden}.actor-head{height:100%;padding:var(--space-3) var(--space-4);justify-content:space-between;align-items:flex-start;display:flex}.actor-identity{flex-direction:column;justify-content:space-between;height:100%;display:flex}.actor-search-wrap{padding:10px var(--space-3)}.actor-search{width:100%;height:30px}.actor-problem{margin:0 var(--space-3) var(--space-3)}.actor-list{min-height:0;padding:0 var(--space-2) var(--space-3);flex:1;overflow-y:auto}.actor-row{width:100%;min-height:var(--row-h);align-items:center;gap:var(--space-2);padding:0 var(--space-2);border-radius:var(--radius);color:var(--ink-2);cursor:pointer;text-align:left;transition:background var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--ease);background:0 0;border:0;display:flex}.actor-row:hover{background:var(--sunken);color:var(--ink)}.actor-row:active{transform:scale(.97)}.actor-row-selected,.actor-row-selected:hover{background:var(--moss-wash);color:var(--moss-ink)}.actor-name{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.actor-kind{color:var(--faint);flex-shrink:0;margin-left:auto;font-size:9.5px}.actor-mark-list{align-items:center;gap:var(--space-1);min-height:0;padding:var(--space-2) 0;flex-direction:column;flex:1;display:flex;overflow-y:auto}.actor-mark-button{width:100%;min-height:var(--row-h);cursor:pointer;background:0 0;border:0;place-items:center;padding:0;display:grid}.actor-mark{border-radius:var(--radius);background:var(--sunken);color:var(--ink-2);transition:color var(--fast) var(--ease), transform var(--fast) var(--ease);place-items:center;font-size:9.5px;display:grid}.actor-mark-button:hover .actor-mark{color:var(--ink)}.actor-mark-button:active .actor-mark{transform:scale(.97)}.actor-mark-selected .actor-mark,.actor-mark-selected:hover .actor-mark{background:var(--moss-wash);color:var(--moss-ink)}.actor-footer{padding:var(--space-2);border-top:1px solid var(--hair);flex-direction:column;flex-shrink:0;gap:2px;margin-top:auto;display:flex}.actor-api{width:100%;min-height:var(--row-h);align-items:center;gap:var(--space-2);padding:0 var(--space-2);border-radius:var(--radius);color:var(--ink-2);cursor:pointer;text-align:left;transition:background var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--ease);background:0 0;border:0;display:flex}.actor-api:hover{background:var(--sunken);color:var(--ink)}.actor-api-active,.actor-api-active:hover{background:var(--moss-wash);color:var(--moss-ink)}.actor-api:active{transform:scale(.97)}.actor-rail-collapsed .actor-api{justify-content:center;padding:0}.api-view{flex:1;min-width:0;height:100%;display:flex;overflow:hidden}.api-nav{background:var(--bg);border-right:1px solid var(--hair);flex-direction:column;min-width:0;display:flex}.api-search-wrap{align-items:center;gap:var(--space-2);height:34px;margin:10px var(--space-3);border:1px solid var(--hair);border-radius:var(--radius);background:var(--bg);color:var(--faint);flex-shrink:0;padding:0 10px;display:flex}.api-search-wrap:focus-within{border-color:var(--moss)}.api-search-wrap input{width:100%;min-width:0;color:var(--ink);background:0 0;border:0;outline:0;padding:0}.api-nav-list{min-height:0;padding:0 var(--space-2) var(--space-3);flex:1;overflow-y:auto}.api-overview-link,.api-nav-row{width:100%;min-height:var(--row-h);align-items:center;gap:var(--space-2);padding:0 var(--space-2);border-radius:var(--radius);color:var(--ink-2);cursor:pointer;text-align:left;transition:background var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--ease);background:0 0;border:0;display:flex}.api-overview-link:hover,.api-nav-row:hover{background:var(--sunken);color:var(--ink)}.api-overview-link:active,.api-nav-row:active{transform:scale(.97)}.api-nav-selected,.api-nav-selected:hover{background:var(--moss-wash);color:var(--moss-ink)}.api-group h2{margin:var(--space-4) var(--space-2) var(--space-1);color:var(--faint);letter-spacing:.07em;text-transform:uppercase;font-size:10.5px;font-weight:400}.api-method{justify-content:center;min-width:42px;font-size:10.5px;font-weight:500;display:inline-flex}.api-method-get{color:var(--wait)}.api-method-post{color:var(--ok)}.api-method-put,.api-method-patch{color:var(--run)}.api-method-delete{color:var(--fail)}.api-nav-path{min-width:0;color:inherit;text-overflow:ellipsis;white-space:nowrap;font-size:11.5px;overflow:hidden}.api-reading{background:var(--bg);flex:1;min-width:0;overflow-y:auto}.api-detail{width:min(1240px,100%);padding:72px var(--space-6) 96px;margin:0 auto}.api-detail-head{padding:var(--space-7) 0 var(--space-5)}.api-operation-title{align-items:center;gap:var(--space-3);display:flex}.api-operation-title h1,.api-overview h1{color:var(--ink);font-size:var(--text-page);letter-spacing:-.02em;margin:0;font-weight:500}.api-operation-detail h1{margin:var(--space-2) 0 0;color:var(--ink);letter-spacing:-.03em;text-transform:lowercase;font-size:30px;font-weight:500}.api-resource{padding-top:var(--space-7);scroll-margin-top:var(--space-5)}.api-resource+.api-resource{margin-top:var(--space-7);border-top:1px solid var(--hair)}.api-endpoint{scroll-margin-top:var(--space-5)}.api-endpoint+.api-endpoint{margin-top:var(--space-7);padding-top:var(--space-7);border-top:1px solid var(--hair)}.api-detail-head h2{color:var(--ink);letter-spacing:-.025em;margin:0;font-size:25px;font-weight:500}.api-operation-id,.api-eyebrow{margin-top:var(--space-2);color:var(--faint);font-size:11px}.api-detail p{max-width:var(--measure);color:var(--ink-2);line-height:1.6}.api-section{padding:var(--space-5) 0;border-bottom:1px solid var(--hair)}.api-section h2,.api-section h3{margin:0 0 var(--space-4);color:var(--ink);font-size:var(--text-read);font-weight:500}.api-tag-overview{align-items:start;gap:var(--space-7);min-height:220px;padding-bottom:var(--space-7);border-bottom:1px solid var(--hair);grid-template-columns:minmax(220px,1fr) minmax(360px,1fr);display:grid}.api-tag-overview .api-eyebrow{color:var(--faint);letter-spacing:.07em;text-transform:uppercase;margin:0;font-size:10.5px}.api-operations-card,.api-code-card{border:1px solid var(--hair);border-radius:var(--radius);background:var(--surface);overflow:hidden}.api-card-title,.api-code-head{min-height:38px;padding:0 var(--space-3);border-bottom:1px solid var(--hair);color:var(--ink-2);font-size:12px}.api-card-title{align-items:center;font-weight:500;display:flex}.api-operations-list{padding:var(--space-2)}.api-operations-list button{align-items:center;gap:var(--space-2);width:100%;min-height:32px;padding:0 var(--space-2);border-radius:var(--radius);color:var(--ink-2);cursor:pointer;text-align:left;background:0 0;border:0;display:flex}.api-operations-list button:hover{background:var(--sunken);color:var(--ink)}.api-operations-list button:active{transform:scale(.97)}.api-operation-grid{gap:var(--space-7);grid-template-columns:minmax(0,1fr) minmax(360px,.95fr);display:grid}.api-operation-docs{min-width:0}.api-operation-examples{top:var(--space-5);align-self:start;gap:var(--space-3);display:grid;position:sticky}.api-code-head{justify-content:space-between;align-items:center;gap:var(--space-3);display:flex}.api-code-head>div{align-items:center;gap:var(--space-2);min-width:0;display:flex}.api-code-head>span{color:var(--faint);font-size:10.5px}.api-code-card pre{max-height:360px;padding:var(--space-4);background:var(--sunken);color:var(--ink-2);white-space:pre-wrap;word-break:break-word;margin:0;font-size:11.5px;line-height:1.65;overflow:auto}.api-parameters,.api-responses,.api-content-list{gap:var(--space-3);display:grid}.api-parameter,.api-response,.api-content{padding:var(--space-3);border:1px solid var(--hair);border-radius:var(--radius);background:var(--surface)}.api-operation-docs .api-section{padding:var(--space-5) 0}.api-operation-docs .api-response{border:0;border-bottom:1px solid var(--hair);background:0 0;border-radius:0;padding:0}.api-operation-docs .api-response:first-child{border-top:1px solid var(--hair)}.api-operation-docs .api-response-head{cursor:pointer;min-height:52px;list-style:none}.api-operation-docs .api-response-head::-webkit-details-marker{display:none}.api-operation-docs .api-response-head:before{border-right:1px solid var(--faint);border-bottom:1px solid var(--faint);content:"";width:7px;height:7px;transition:transform var(--fast) var(--ease);transform:rotate(-45deg)}.api-operation-docs .api-response[open] .api-response-head:before{transform:rotate(45deg)}.api-operation-docs .api-response .api-content-list{margin:0 0 var(--space-4) 22px}.api-operation-docs .api-response .api-content{background:0 0;border:0}.api-operation-docs .api-request-section .api-content{padding:var(--space-3) 0;border:0;border-top:1px solid var(--hair);border-bottom:1px solid var(--hair);background:0 0;border-radius:0}.api-operation-docs .api-request-section .api-media-type{margin-bottom:var(--space-3)}.api-operation-docs .api-parameters-section .api-parameter{padding:var(--space-3) 0;border:0;border-top:1px solid var(--hair);background:0 0;border-radius:0}.api-operation-docs .api-parameters-section .api-parameter:last-child{border-bottom:1px solid var(--hair)}.api-headers-section{padding-top:0}.api-header-line{border-top:1px solid var(--hair);grid-template-columns:120px minmax(0,1fr);align-items:center;min-height:44px;display:grid}.api-header-line:last-child{border-bottom:1px solid var(--hair)}.api-header-example{color:var(--faint);font-size:11.5px}.api-parameter{align-items:center;gap:var(--space-3);grid-template-columns:minmax(140px,1fr) minmax(100px,auto) auto;display:grid}.api-parameter p{grid-column:1/-1;margin:0}.api-parameter-in{margin-left:var(--space-2);color:var(--faint);font-size:10.5px}.api-response-head{align-items:center;gap:var(--space-3);display:flex}.api-status{color:var(--fail);font-weight:500}.api-status-ok{color:var(--ok)}.api-response .api-content-list{margin-top:var(--space-3)}.api-media-type{margin-bottom:var(--space-2);color:var(--faint);font-size:11px}.api-type,.api-property-type{color:var(--wait);font-size:11.5px}.api-properties{margin-top:var(--space-2);border-top:1px solid var(--hair)}.api-property{padding:10px 0 2px var(--space-3);border-left:1px solid var(--hair)}.api-property-line{align-items:baseline;gap:var(--space-2);display:flex}.api-property-name{color:var(--ink);font-size:12px}.api-required,.api-optional{color:var(--faint);margin-left:auto;font-size:10px}.api-required{color:var(--run)}.api-schema-cut{margin-top:var(--space-2);color:var(--faint);font-size:11px}.api-overview{padding:var(--space-4) 0 var(--space-7);scroll-margin-top:var(--space-5);border-bottom:1px solid var(--hair)}.api-overview .api-eyebrow{margin:0 0 var(--space-2);letter-spacing:.07em;text-transform:uppercase}.api-version{margin-top:var(--space-2);color:var(--faint);font-size:11px;display:inline-block}.api-facts{margin-top:var(--space-6);border-top:1px solid var(--hair);gap:0;display:grid}.api-facts div{padding:var(--space-3) 0;border-bottom:1px solid var(--hair);grid-template-columns:140px minmax(0,1fr);display:grid}.api-facts dt{color:var(--faint)}.api-facts dd{overflow-wrap:anywhere;margin:0}.api-load-problem{margin:var(--space-7)}@media (width<=760px){.api-detail{padding-right:var(--space-4);padding-left:var(--space-4)}}@media (width<=1100px){.api-tag-overview,.api-operation-grid{grid-template-columns:1fr}.api-tag-overview{min-height:0}.api-operation-examples{position:static}}.actor-rail-collapsed .actor-only{display:none}.actor-rail-collapsed .actor-head{justify-content:center}.rail-search{width:100%;height:30px}.run-list{min-height:0;padding:0 var(--space-2) var(--space-3);flex:1;overflow-y:auto}.rail-row{padding:var(--space-2) 14px;border-radius:var(--radius);cursor:pointer;transition:background var(--fast) var(--ease);flex-direction:column;gap:2px;display:flex}.rail-row:hover{background:var(--sunken)}.rail-row-selected,.rail-row-selected:hover{background:var(--moss-wash)}.rail-id{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;font-size:12.5px;overflow:hidden}.rail-meta{color:var(--faint);font-size:11px}.thread-head{align-items:center;gap:var(--space-2);padding:var(--space-4) calc(var(--space-5) + 34px) 10px var(--space-5);display:flex}.icon-btn{border-radius:var(--radius);width:26px;height:26px;color:var(--faint);cursor:pointer;transition:color var(--fast) var(--ease);background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.icon-btn:hover{color:var(--ink-2)}.icon-btn:active{transform:scale(.97)}.icon-btn-done{color:var(--ok)}.window{margin:0 var(--space-5) 10px;-webkit-user-select:none;user-select:none}.window-head{justify-content:space-between;align-items:baseline;margin-bottom:6px;display:flex}.window-eyebrow{color:var(--faint);text-transform:uppercase;letter-spacing:.06em;font-size:11px}.window-actions{align-items:center;gap:var(--space-2);display:flex}.window-readout{color:var(--ink-2);font-size:11.5px}.window-count{color:var(--faint)}.window-track{position:relative}.window-bars{align-items:flex-end;gap:1px;display:flex;position:absolute;inset:0}.window-bar{background:var(--hair);transition:background var(--fast) var(--ease);border-radius:1px 1px 0 0;flex:1}.window-bar-inside{background:var(--moss);opacity:.55}.window-selection{background:color-mix(in oklab, var(--moss) 12%, transparent);border-left:2px solid var(--moss);border-right:2px solid var(--moss);pointer-events:none;position:absolute;top:0;bottom:0}.window-handle{cursor:ew-resize;width:12px;margin-left:-6px;position:absolute;top:0;bottom:0}.window-grip{background:var(--moss);width:2px;height:100%;margin:0 auto}.window-marks{margin-top:var(--space-1);color:var(--faint);justify-content:space-between;font-size:10px;display:flex}.stream-note{padding:0 var(--space-5) var(--space-2);color:var(--faint);font-size:11px}.thread-view{flex:1;min-width:0;min-height:0;display:flex}.thread-trace{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.events{min-width:0;padding:0 var(--space-4) var(--space-4);flex:1;overflow-y:auto}.event-row{align-items:flex-start;gap:var(--space-3);padding:9px var(--space-2);border-radius:var(--radius);cursor:pointer;transition:background var(--fast) var(--ease);display:flex}.event-row:hover{background:var(--sunken)}.event-row-selected,.event-row-selected:hover{background:var(--moss-wash)}.event-stamp{border-radius:var(--radius-chip);white-space:nowrap;flex-shrink:0;align-items:center;height:24px;padding:0 10px;font-size:12px;display:inline-flex}.event-text{min-width:0;color:var(--ink);white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:14.5px;line-height:1.55;overflow:hidden}.event-time{padding-left:var(--space-4);color:var(--faint);flex-shrink:0;margin-top:4px;margin-left:auto;font-size:11px}.event-inspector{border-left:1px solid var(--hair);background:var(--bg);flex:0 auto;min-width:0;overflow-y:auto}.event-inspector-head{padding:0 var(--space-4);border-bottom:1px solid var(--hair);flex-shrink:0;justify-content:space-between;align-items:center;display:flex}.event-inspector-title{align-items:center;gap:var(--space-2);min-width:0;display:flex}.event-inspector-seq{color:var(--faint);white-space:nowrap;font-size:11px}.event-detail{margin:var(--space-4);padding-left:var(--space-3);border-left:1px solid var(--hair);grid-template-columns:max-content minmax(0,1fr);gap:5px 20px;display:grid}.event-key{color:var(--faint);font-size:11.5px}.event-value{color:var(--ink);white-space:pre-wrap;word-break:break-word;font-size:12.5px;display:block}.event-value-frame{min-width:0}.field-more{background:linear-gradient(to bottom, transparent, var(--bg) 68%);justify-content:center;margin-top:-64px;padding:46px 0 4px;display:flex;position:relative}.field-toggle{min-height:28px;margin:var(--space-2) auto 0;padding:0 var(--space-3);border:1px solid var(--hair);border-radius:var(--radius-chip);background:var(--surface);color:var(--ink-2);cursor:pointer;font-family:var(--sans);font-size:var(--text-meta);box-shadow:var(--shadow);transition:color var(--fast) var(--ease), transform var(--fast) var(--ease);display:block}.field-toggle:hover{color:var(--ink)}.field-toggle:active{transform:scale(.97)}.event-code{background:var(--sunken);border-radius:4px;padding:8px 10px;line-height:1.5}.event-json{tab-size:2;line-height:1.55}.pane-empty{padding:var(--space-7) 0;text-align:center;font-size:var(--text-meta);color:var(--faint)}.quickstart-empty{min-height:0;padding:var(--space-6);flex:1;place-items:center;display:grid}.quickstart{width:min(720px,100%)}.quickstart-intro{margin-bottom:var(--space-5);text-align:center}.quickstart-intro h1{margin:0 0 var(--space-2);font-size:var(--text-page);letter-spacing:-.02em;font-weight:500}.quickstart-intro p{color:var(--ink-2);font-size:var(--text-read);margin:0}.quickstart-grid{gap:var(--space-3);display:grid}.quickstart-card{border:1px solid var(--hair);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow);overflow:hidden}.quickstart-card-head{min-height:38px;padding:0 var(--space-3) 0 var(--space-4);border-bottom:1px solid var(--hair);justify-content:space-between;align-items:center;display:flex}.quickstart-label{color:var(--ink-2);text-transform:uppercase;letter-spacing:.06em;font-size:11px}.quickstart-copy{min-height:26px;padding:0 var(--space-2);border-radius:var(--radius);color:var(--faint);cursor:pointer;font-size:var(--text-meta);transition:color var(--fast) var(--ease), transform var(--fast) var(--ease);background:0 0;border:0;align-items:center;gap:6px;display:inline-flex}.quickstart-copy:hover{color:var(--ink-2)}.quickstart-copy:active{transform:scale(.97)}.quickstart-copy-done{color:var(--ok)}.quickstart-code{padding:var(--space-4);background:var(--sunken);color:var(--ink);font-family:var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12.5px;line-height:1.6;overflow-x:auto}@media (width<=640px){.quickstart-empty{padding:var(--space-5) var(--space-4);align-items:start}}
|