agent-screenplay 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +141 -2
- package/assets/editor/index.html +536 -0
- package/assets/fonts/Inter-Bold.ttf +0 -0
- package/assets/fonts/Inter-LICENSE.txt +92 -0
- package/assets/fonts/Inter-Medium.ttf +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +163 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.d.ts +16 -0
- package/dist/core.js +73 -0
- package/dist/core.js.map +1 -0
- package/dist/direct/plan.d.ts +100 -0
- package/dist/direct/plan.js +274 -0
- package/dist/direct/plan.js.map +1 -0
- package/dist/drivers/browser.d.ts +9 -0
- package/dist/drivers/browser.js +44 -0
- package/dist/drivers/browser.js.map +1 -0
- package/dist/drivers/desktop.d.ts +53 -0
- package/dist/drivers/desktop.js +650 -0
- package/dist/drivers/desktop.js.map +1 -0
- package/dist/drivers/pace.d.ts +11 -0
- package/dist/drivers/pace.js +44 -0
- package/dist/drivers/pace.js.map +1 -0
- package/dist/drivers/web.d.ts +37 -0
- package/dist/drivers/web.js +570 -0
- package/dist/drivers/web.js.map +1 -0
- package/dist/editor/server.d.ts +96 -0
- package/dist/editor/server.js +303 -0
- package/dist/editor/server.js.map +1 -0
- package/dist/ffmpeg.d.ts +5 -0
- package/dist/ffmpeg.js +44 -0
- package/dist/ffmpeg.js.map +1 -0
- package/dist/guide.d.ts +6 -0
- package/dist/guide.js +25 -0
- package/dist/guide.js.map +1 -0
- package/dist/init.d.ts +19 -0
- package/dist/init.js +147 -0
- package/dist/init.js.map +1 -0
- package/dist/login.d.ts +2 -0
- package/dist/login.js +50 -0
- package/dist/login.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +132 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/render/compose.d.ts +68 -0
- package/dist/render/compose.js +361 -0
- package/dist/render/compose.js.map +1 -0
- package/dist/render/presets.d.ts +42 -0
- package/dist/render/presets.js +111 -0
- package/dist/render/presets.js.map +1 -0
- package/dist/render/render.d.ts +25 -0
- package/dist/render/render.js +257 -0
- package/dist/render/render.js.map +1 -0
- package/dist/render/sources.d.ts +15 -0
- package/dist/render/sources.js +146 -0
- package/dist/render/sources.js.map +1 -0
- package/dist/render/style.d.ts +37 -0
- package/dist/render/style.js +118 -0
- package/dist/render/style.js.map +1 -0
- package/dist/scenario/config.d.ts +43 -0
- package/dist/scenario/config.js +69 -0
- package/dist/scenario/config.js.map +1 -0
- package/dist/scenario/fingerprint.d.ts +10 -0
- package/dist/scenario/fingerprint.js +58 -0
- package/dist/scenario/fingerprint.js.map +1 -0
- package/dist/scenario/load.d.ts +36 -0
- package/dist/scenario/load.js +101 -0
- package/dist/scenario/load.js.map +1 -0
- package/dist/scenario/schema.d.ts +553 -0
- package/dist/scenario/schema.js +262 -0
- package/dist/scenario/schema.js.map +1 -0
- package/dist/scenario/target.d.ts +26 -0
- package/dist/scenario/target.js +40 -0
- package/dist/scenario/target.js.map +1 -0
- package/dist/take.d.ts +100 -0
- package/dist/take.js +15 -0
- package/dist/take.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/voice/cartesia.d.ts +12 -0
- package/dist/voice/cartesia.js +103 -0
- package/dist/voice/cartesia.js.map +1 -0
- package/package.json +65 -4
- package/schema/scenario.schema.json +1098 -0
- package/skills/screenplay/SKILL.md +127 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Style } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* A project's shared settings: `screenplay.config.json`, found in the scenario's
|
|
5
|
+
* directory or the nearest parent. Its style applies to every video below it; a
|
|
6
|
+
* scenario's own `style` wins, setting by setting.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CONFIG_FILE = "screenplay.config.json";
|
|
9
|
+
export declare const ProjectConfig: z.ZodObject<{
|
|
10
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
11
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
background: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
13
|
+
gradient: z.ZodArray<z.ZodString>;
|
|
14
|
+
angle: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
}, z.core.$strict>]>>;
|
|
16
|
+
padding: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
shadow: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
cursor: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
dot: "dot";
|
|
21
|
+
macos: "macos";
|
|
22
|
+
none: "none";
|
|
23
|
+
}>>;
|
|
24
|
+
cursorSize: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
maxZoom: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
autoZoom: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
captions: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
29
|
+
frame: z.ZodOptional<z.ZodEnum<{
|
|
30
|
+
browser: "browser";
|
|
31
|
+
none: "none";
|
|
32
|
+
}>>;
|
|
33
|
+
}, z.core.$strict>>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
export type ProjectConfig = z.infer<typeof ProjectConfig>;
|
|
36
|
+
export declare function findProjectConfig(dir: string): string | undefined;
|
|
37
|
+
export declare function readProjectConfig(file: string): ProjectConfig;
|
|
38
|
+
/** True when a background value names an image file rather than a preset or a color. */
|
|
39
|
+
export declare function isImagePath(bg: unknown, presets: readonly string[]): bg is string;
|
|
40
|
+
/** Make an image background absolute, so it still resolves once merged into a scenario elsewhere. */
|
|
41
|
+
export declare function absolutizeStyle(style: Style | undefined, baseDir: string, presets: readonly string[]): Style | undefined;
|
|
42
|
+
/** The project's style under the scenario's, setting by setting. */
|
|
43
|
+
export declare function mergeStyles(project: Style | undefined, scenario: Style | undefined): Style | undefined;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { Style } from "./schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* A project's shared settings: `screenplay.config.json`, found in the scenario's
|
|
7
|
+
* directory or the nearest parent. Its style applies to every video below it; a
|
|
8
|
+
* scenario's own `style` wins, setting by setting.
|
|
9
|
+
*/
|
|
10
|
+
export const CONFIG_FILE = "screenplay.config.json";
|
|
11
|
+
export const ProjectConfig = z
|
|
12
|
+
.object({
|
|
13
|
+
$schema: z.string().optional(),
|
|
14
|
+
style: Style.optional(),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
17
|
+
export function findProjectConfig(dir) {
|
|
18
|
+
let d = path.resolve(dir);
|
|
19
|
+
while (true) {
|
|
20
|
+
const f = path.join(d, CONFIG_FILE);
|
|
21
|
+
if (fs.existsSync(f))
|
|
22
|
+
return f;
|
|
23
|
+
const up = path.dirname(d);
|
|
24
|
+
if (up === d)
|
|
25
|
+
return undefined;
|
|
26
|
+
d = up;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function readProjectConfig(file) {
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new Error(`${file} is not valid JSON: ${e.message}`);
|
|
36
|
+
}
|
|
37
|
+
const r = ProjectConfig.safeParse(raw);
|
|
38
|
+
if (!r.success) {
|
|
39
|
+
const issues = r.error.issues.map((i) => `- ${i.path.map(String).join(".") || "(file)"}: ${i.message}`).join("\n");
|
|
40
|
+
throw new Error(`${file} is not valid:\n${issues}`);
|
|
41
|
+
}
|
|
42
|
+
return r.data;
|
|
43
|
+
}
|
|
44
|
+
/** True when a background value names an image file rather than a preset or a color. */
|
|
45
|
+
export function isImagePath(bg, presets) {
|
|
46
|
+
return typeof bg === "string" && !presets.includes(bg) && !/^(#|rgb|hsl)/i.test(bg);
|
|
47
|
+
}
|
|
48
|
+
/** Make an image background absolute, so it still resolves once merged into a scenario elsewhere. */
|
|
49
|
+
export function absolutizeStyle(style, baseDir, presets) {
|
|
50
|
+
if (!style)
|
|
51
|
+
return style;
|
|
52
|
+
if (isImagePath(style.background, presets) && !path.isAbsolute(style.background)) {
|
|
53
|
+
return { ...style, background: path.resolve(baseDir, style.background) };
|
|
54
|
+
}
|
|
55
|
+
return style;
|
|
56
|
+
}
|
|
57
|
+
/** The project's style under the scenario's, setting by setting. */
|
|
58
|
+
export function mergeStyles(project, scenario) {
|
|
59
|
+
if (!project)
|
|
60
|
+
return scenario;
|
|
61
|
+
if (!scenario)
|
|
62
|
+
return project;
|
|
63
|
+
const out = { ...project };
|
|
64
|
+
for (const [k, v] of Object.entries(scenario))
|
|
65
|
+
if (v !== undefined)
|
|
66
|
+
out[k] = v;
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/scenario/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Scenario } from "./schema.js";
|
|
2
|
+
export declare function captureInputs(scenario: Scenario): {
|
|
3
|
+
setup: string;
|
|
4
|
+
steps: string[];
|
|
5
|
+
};
|
|
6
|
+
/** Why the scenario cannot be re-rendered from a take, or undefined when it can. */
|
|
7
|
+
export declare function captureMismatch(scenario: Scenario, recorded: {
|
|
8
|
+
setup: string;
|
|
9
|
+
steps: string[];
|
|
10
|
+
} | undefined, recordedKinds: string[]): string | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { describeStep, stepKind } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* What a recording depends on. Editing-only fields (narration, captions,
|
|
5
|
+
* callout text, card text, zoom, style, voice, output) are left out, so they
|
|
6
|
+
* can change and be re-rendered without filming again; anything else that
|
|
7
|
+
* changes (a target, typed text, a wait, a hold) needs a new recording.
|
|
8
|
+
*/
|
|
9
|
+
const EDITORIAL_KEYS = new Set(["say", "zoom", "text"]);
|
|
10
|
+
const CARD_KEYS = new Set(["title", "end", "subtitle", "duration", "say"]);
|
|
11
|
+
export function captureInputs(scenario) {
|
|
12
|
+
const setup = JSON.stringify([
|
|
13
|
+
scenario.url ?? null,
|
|
14
|
+
scenario.app ?? null,
|
|
15
|
+
scenario.viewport ?? null,
|
|
16
|
+
scenario.scale ?? null,
|
|
17
|
+
scenario.colorScheme ?? null,
|
|
18
|
+
scenario.locale ?? null,
|
|
19
|
+
scenario.auth ?? null,
|
|
20
|
+
scenario.hideScrollbars ?? null,
|
|
21
|
+
scenario.typingSpeed ?? null,
|
|
22
|
+
// Appended only when set, so takes recorded before these options existed keep their fingerprint.
|
|
23
|
+
...(scenario.screen || scenario.hideOthers ? [scenario.screen ?? null, scenario.hideOthers ?? null] : []),
|
|
24
|
+
]);
|
|
25
|
+
const steps = scenario.steps.map((step) => {
|
|
26
|
+
const kind = stepKind(step);
|
|
27
|
+
const skip = kind === "title" || kind === "end" ? CARD_KEYS : EDITORIAL_KEYS;
|
|
28
|
+
const kept = Object.entries(step)
|
|
29
|
+
.filter(([k]) => !skip.has(k))
|
|
30
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
31
|
+
return hash(JSON.stringify([kind, kept]));
|
|
32
|
+
});
|
|
33
|
+
return { setup: hash(setup), steps };
|
|
34
|
+
}
|
|
35
|
+
function hash(s) {
|
|
36
|
+
return crypto.createHash("sha1").update(s).digest("hex").slice(0, 12);
|
|
37
|
+
}
|
|
38
|
+
/** Why the scenario cannot be re-rendered from a take, or undefined when it can. */
|
|
39
|
+
export function captureMismatch(scenario, recorded, recordedKinds) {
|
|
40
|
+
const now = captureInputs(scenario);
|
|
41
|
+
if (scenario.steps.length !== recordedKinds.length) {
|
|
42
|
+
return `the scenario has ${scenario.steps.length} steps and the recording ${recordedKinds.length}`;
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < scenario.steps.length; i++) {
|
|
45
|
+
if (stepKind(scenario.steps[i]) !== recordedKinds[i])
|
|
46
|
+
return `step ${i + 1} (${describeStep(scenario.steps[i])}) changed`;
|
|
47
|
+
}
|
|
48
|
+
if (!recorded)
|
|
49
|
+
return undefined; // takes from before fingerprints: kinds are all we can compare
|
|
50
|
+
if (recorded.setup !== now.setup)
|
|
51
|
+
return "the page, app, screen mode, viewport, session or typing speed changed";
|
|
52
|
+
for (let i = 0; i < now.steps.length; i++) {
|
|
53
|
+
if (recorded.steps[i] !== now.steps[i])
|
|
54
|
+
return `step ${i + 1} (${describeStep(scenario.steps[i])}) changed`;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/scenario/fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Scenario } from "./schema.js";
|
|
2
|
+
export interface LoadedScenario {
|
|
3
|
+
scenario: Scenario;
|
|
4
|
+
/** Absolute path of the scenario file. */
|
|
5
|
+
file: string;
|
|
6
|
+
/** Directory of the scenario file. */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** Video name: scenario.name or the file name without extension. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Working directory for takes and caches: <dir>/.screenplay */
|
|
11
|
+
workDir: string;
|
|
12
|
+
/** Directory of the latest take of this scenario. */
|
|
13
|
+
takeDir: string;
|
|
14
|
+
outputs: {
|
|
15
|
+
mp4: string;
|
|
16
|
+
gif: string;
|
|
17
|
+
storyboard: string;
|
|
18
|
+
};
|
|
19
|
+
/** The project's screenplay.config.json, when one applies (its style is merged into `scenario.style`). */
|
|
20
|
+
projectConfig?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class ScenarioError extends Error {
|
|
23
|
+
}
|
|
24
|
+
/** Replace ${VAR} with environment values in every string of the scenario. */
|
|
25
|
+
export declare function substituteEnv<T>(value: T, env?: NodeJS.ProcessEnv): T;
|
|
26
|
+
export declare function formatZodError(err: {
|
|
27
|
+
issues: Array<{
|
|
28
|
+
path: PropertyKey[];
|
|
29
|
+
message: string;
|
|
30
|
+
}>;
|
|
31
|
+
}): string;
|
|
32
|
+
export declare function parseScenario(raw: unknown): Scenario;
|
|
33
|
+
export declare function loadScenario(file: string, opts?: {
|
|
34
|
+
env?: boolean;
|
|
35
|
+
}): LoadedScenario;
|
|
36
|
+
export declare function slugify(s: string): string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { BACKGROUNDS, Scenario } from "./schema.js";
|
|
5
|
+
import { absolutizeStyle, findProjectConfig, mergeStyles, readProjectConfig } from "./config.js";
|
|
6
|
+
export class ScenarioError extends Error {
|
|
7
|
+
}
|
|
8
|
+
const ENV_RE = /\$\{([A-Z0-9_]+)\}/g;
|
|
9
|
+
/** Replace ${VAR} with environment values in every string of the scenario. */
|
|
10
|
+
export function substituteEnv(value, env = process.env) {
|
|
11
|
+
if (typeof value === "string") {
|
|
12
|
+
return value.replace(ENV_RE, (_, name) => {
|
|
13
|
+
const v = env[name];
|
|
14
|
+
if (v === undefined)
|
|
15
|
+
throw new ScenarioError(`Environment variable ${name} is not set (used as \${${name}} in the scenario).`);
|
|
16
|
+
return v;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(value))
|
|
20
|
+
return value.map((v) => substituteEnv(v, env));
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const [k, v] of Object.entries(value))
|
|
24
|
+
out[k] = substituteEnv(v, env);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
export function formatZodError(err) {
|
|
30
|
+
return err.issues
|
|
31
|
+
.slice(0, 8)
|
|
32
|
+
.map((i) => `- ${i.path.length ? i.path.map(String).join(".") : "(scenario)"}: ${i.message}`)
|
|
33
|
+
.join("\n");
|
|
34
|
+
}
|
|
35
|
+
export function parseScenario(raw) {
|
|
36
|
+
const result = Scenario.safeParse(raw);
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
throw new ScenarioError(`The scenario is not valid:\n${formatZodError(result.error)}\nRun \`screenplay schema\` for the full format.`);
|
|
39
|
+
}
|
|
40
|
+
return result.data;
|
|
41
|
+
}
|
|
42
|
+
export function loadScenario(file, opts = {}) {
|
|
43
|
+
const given = path.resolve(file);
|
|
44
|
+
if (!fs.existsSync(given))
|
|
45
|
+
throw new ScenarioError(`Scenario not found: ${given}`);
|
|
46
|
+
// The real path, so the same scenario reached through a symlink (macOS /tmp is /private/tmp) is the same scenario.
|
|
47
|
+
const abs = fs.realpathSync(given);
|
|
48
|
+
let raw;
|
|
49
|
+
try {
|
|
50
|
+
raw = JSON.parse(fs.readFileSync(abs, "utf8"));
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
throw new ScenarioError(`The scenario is not valid JSON: ${e.message}`);
|
|
54
|
+
}
|
|
55
|
+
let scenario = parseScenario(raw);
|
|
56
|
+
if (opts.env !== false)
|
|
57
|
+
scenario = substituteEnv(scenario);
|
|
58
|
+
const dir = path.dirname(abs);
|
|
59
|
+
// A url without a scheme is a local file, relative to the scenario.
|
|
60
|
+
if (scenario.url && !/^[a-z][a-z0-9+.-]*:/i.test(scenario.url)) {
|
|
61
|
+
scenario = { ...scenario, url: pathToFileURL(path.resolve(dir, scenario.url)).toString() };
|
|
62
|
+
}
|
|
63
|
+
const projectConfig = findProjectConfig(dir);
|
|
64
|
+
if (projectConfig) {
|
|
65
|
+
let config;
|
|
66
|
+
try {
|
|
67
|
+
config = readProjectConfig(projectConfig);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
throw new ScenarioError(e.message);
|
|
71
|
+
}
|
|
72
|
+
const projectStyle = absolutizeStyle(config.style, path.dirname(projectConfig), BACKGROUNDS);
|
|
73
|
+
scenario = { ...scenario, style: mergeStyles(projectStyle, scenario.style) };
|
|
74
|
+
}
|
|
75
|
+
const base = path.basename(abs).replace(/\.json$/i, "").replace(/\.screenplay$/i, "");
|
|
76
|
+
const name = scenario.name ?? base;
|
|
77
|
+
const slug = slugify(name);
|
|
78
|
+
const workDir = path.join(dir, ".screenplay");
|
|
79
|
+
const mp4 = scenario.output?.path ? path.resolve(dir, scenario.output.path) : path.join(dir, `${slug}.mp4`);
|
|
80
|
+
const stem = mp4.replace(/\.mp4$/i, "");
|
|
81
|
+
return {
|
|
82
|
+
scenario,
|
|
83
|
+
file: abs,
|
|
84
|
+
dir,
|
|
85
|
+
name,
|
|
86
|
+
workDir,
|
|
87
|
+
takeDir: path.join(workDir, "takes", slug),
|
|
88
|
+
outputs: { mp4, gif: `${stem}.gif`, storyboard: `${stem}.storyboard.png` },
|
|
89
|
+
projectConfig,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function slugify(s) {
|
|
93
|
+
return (s
|
|
94
|
+
.normalize("NFKD")
|
|
95
|
+
.replace(/[̀-ͯ]/g, "")
|
|
96
|
+
.toLowerCase()
|
|
97
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
98
|
+
.replace(/^-+|-+$/g, "")
|
|
99
|
+
.slice(0, 60) || "screenplay");
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/scenario/load.ts"],"names":[],"mappings":"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"}
|