@aicopycoders/exodus 2026.6.2400

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.
Files changed (114) hide show
  1. package/assets/references/awareness-framework.md +156 -0
  2. package/assets/references/cast-video.md +165 -0
  3. package/assets/references/copy-blocks.md +138 -0
  4. package/assets/references/creative-strategy.md +990 -0
  5. package/assets/references/editing-rules.md +81 -0
  6. package/assets/references/hook-quality-checklist.md +153 -0
  7. package/assets/references/iterations.md +73 -0
  8. package/assets/references/scrawls.md +483 -0
  9. package/assets/references/segments.md +79 -0
  10. package/assets/skills/exodus/SKILL.md +50 -0
  11. package/assets/skills/exodus-brand/SKILL.md +117 -0
  12. package/assets/skills/exodus-browse/SKILL.md +96 -0
  13. package/assets/skills/exodus-creative/SKILL.md +94 -0
  14. package/assets/skills/exodus-drive/SKILL.md +118 -0
  15. package/assets/skills/exodus-foundation/SKILL.md +170 -0
  16. package/assets/skills/exodus-genesis/SKILL.md +159 -0
  17. package/assets/skills/exodus-idea/SKILL.md +78 -0
  18. package/assets/skills/exodus-image/SKILL.md +224 -0
  19. package/assets/skills/exodus-meme/SKILL.md +183 -0
  20. package/assets/skills/exodus-primer/SKILL.md +145 -0
  21. package/assets/skills/exodus-strategist/SKILL.md +217 -0
  22. package/assets/skills/exodus-swipe/SKILL.md +85 -0
  23. package/assets/skills/exodus-template/SKILL.md +106 -0
  24. package/assets/skills/exodus-write/SKILL.md +157 -0
  25. package/assets/skills/genesis-bots/.env.example +12 -0
  26. package/assets/skills/genesis-bots/README.txt +29 -0
  27. package/assets/skills/genesis-bots/SKILL.md +219 -0
  28. package/assets/skills/genesis-bots/scripts/genesis-stream.mjs +307 -0
  29. package/assets/skills/genesis-bots/scripts/genesis-stream.py +220 -0
  30. package/dist/bin/exodus.d.ts +2 -0
  31. package/dist/bin/exodus.js +177 -0
  32. package/dist/commands/__tests__/genesis.hookgate.test.d.ts +1 -0
  33. package/dist/commands/__tests__/genesis.hookgate.test.js +12 -0
  34. package/dist/commands/brand.d.ts +24 -0
  35. package/dist/commands/brand.js +292 -0
  36. package/dist/commands/browse.d.ts +9 -0
  37. package/dist/commands/browse.js +132 -0
  38. package/dist/commands/creative.d.ts +2 -0
  39. package/dist/commands/creative.js +223 -0
  40. package/dist/commands/doctor.d.ts +21 -0
  41. package/dist/commands/doctor.js +590 -0
  42. package/dist/commands/drive.d.ts +2 -0
  43. package/dist/commands/drive.js +154 -0
  44. package/dist/commands/foundation.d.ts +2 -0
  45. package/dist/commands/foundation.js +468 -0
  46. package/dist/commands/genesis.d.ts +64 -0
  47. package/dist/commands/genesis.js +564 -0
  48. package/dist/commands/idea.d.ts +53 -0
  49. package/dist/commands/idea.js +333 -0
  50. package/dist/commands/image.d.ts +2 -0
  51. package/dist/commands/image.js +249 -0
  52. package/dist/commands/init.d.ts +13 -0
  53. package/dist/commands/init.js +70 -0
  54. package/dist/commands/keys.d.ts +2 -0
  55. package/dist/commands/keys.js +101 -0
  56. package/dist/commands/meme.d.ts +12 -0
  57. package/dist/commands/meme.js +272 -0
  58. package/dist/commands/migrate.d.ts +3 -0
  59. package/dist/commands/migrate.js +158 -0
  60. package/dist/commands/primer.d.ts +2 -0
  61. package/dist/commands/primer.js +540 -0
  62. package/dist/commands/read-doc.d.ts +3 -0
  63. package/dist/commands/read-doc.js +62 -0
  64. package/dist/commands/status.d.ts +6 -0
  65. package/dist/commands/status.js +205 -0
  66. package/dist/commands/swipe.d.ts +2 -0
  67. package/dist/commands/swipe.js +398 -0
  68. package/dist/commands/template.d.ts +2 -0
  69. package/dist/commands/template.js +225 -0
  70. package/dist/commands/whoami.d.ts +2 -0
  71. package/dist/commands/whoami.js +51 -0
  72. package/dist/lib/ad-library-write.d.ts +15 -0
  73. package/dist/lib/ad-library-write.js +67 -0
  74. package/dist/lib/assets.d.ts +3 -0
  75. package/dist/lib/assets.js +23 -0
  76. package/dist/lib/backend-hint.d.ts +2 -0
  77. package/dist/lib/backend-hint.js +22 -0
  78. package/dist/lib/cc-command.d.ts +1 -0
  79. package/dist/lib/cc-command.js +13 -0
  80. package/dist/lib/client.d.ts +23 -0
  81. package/dist/lib/client.js +213 -0
  82. package/dist/lib/format.d.ts +8 -0
  83. package/dist/lib/format.js +172 -0
  84. package/dist/lib/intel-format.d.ts +1 -0
  85. package/dist/lib/intel-format.js +38 -0
  86. package/dist/lib/keys-sync.d.ts +16 -0
  87. package/dist/lib/keys-sync.js +88 -0
  88. package/dist/lib/layout.d.ts +38 -0
  89. package/dist/lib/layout.js +153 -0
  90. package/dist/lib/load-env.d.ts +1 -0
  91. package/dist/lib/load-env.js +47 -0
  92. package/dist/lib/pixar-orchestrator-types.d.ts +54 -0
  93. package/dist/lib/pixar-orchestrator-types.js +1 -0
  94. package/dist/lib/poll.d.ts +14 -0
  95. package/dist/lib/poll.js +32 -0
  96. package/dist/lib/preflight.d.ts +18 -0
  97. package/dist/lib/preflight.js +56 -0
  98. package/dist/lib/prompts.d.ts +9 -0
  99. package/dist/lib/prompts.js +99 -0
  100. package/dist/lib/reel-write.d.ts +14 -0
  101. package/dist/lib/reel-write.js +59 -0
  102. package/dist/lib/release.d.ts +4 -0
  103. package/dist/lib/release.js +81 -0
  104. package/dist/lib/scaffold.d.ts +8 -0
  105. package/dist/lib/scaffold.js +67 -0
  106. package/dist/lib/scout-hydrate.d.ts +1 -0
  107. package/dist/lib/scout-hydrate.js +10 -0
  108. package/dist/lib/state.d.ts +6 -0
  109. package/dist/lib/state.js +49 -0
  110. package/dist/lib/template-constants.d.ts +4 -0
  111. package/dist/lib/template-constants.js +85 -0
  112. package/dist/lib/version.d.ts +1 -0
  113. package/dist/lib/version.js +19 -0
  114. package/package.json +30 -0
@@ -0,0 +1,172 @@
1
+ function stepIcon(status) {
2
+ if (status === "completed" || status === "done")
3
+ return "✓";
4
+ if (status === "failed" || status === "error")
5
+ return "✗";
6
+ return "…";
7
+ }
8
+ function formatSteps(steps) {
9
+ if (!Array.isArray(steps) || steps.length === 0)
10
+ return "";
11
+ const lines = steps.map((s) => {
12
+ if (typeof s !== "object" || s === null)
13
+ return ` ${stepIcon(null)} ${String(s)}`;
14
+ const step = s;
15
+ const name = step["label"] ?? step["name"] ?? step["key"] ?? step["step"] ?? step["id"] ?? "Step";
16
+ const status = step["status"];
17
+ const icon = stepIcon(status);
18
+ const detail = step["detail"] ?? step["message"];
19
+ const error = step["error"];
20
+ const parts = [];
21
+ if (detail)
22
+ parts.push(String(detail));
23
+ if (error && error !== detail)
24
+ parts.push(`error: ${String(error)}`);
25
+ const extra = parts.length > 0 ? ` — ${parts.join(" | ")}` : "";
26
+ return ` ${icon} ${String(name)}${extra}`;
27
+ });
28
+ return "\n**Steps:**\n" + lines.join("\n");
29
+ }
30
+ export function formatGeneration(data) {
31
+ const lines = [];
32
+ const status = data["status"] ?? "unknown";
33
+ lines.push(`## Generation Result`);
34
+ lines.push(`**Status:** ${status}`);
35
+ if (data["agentName"] || data["agent"]) {
36
+ lines.push(`**Agent:** ${data["agentName"] ?? data["agent"]}`);
37
+ }
38
+ const vType = data["variationType"];
39
+ const nVar = data["numVariations"];
40
+ if (typeof vType === "string" && typeof nVar === "number") {
41
+ lines.push(`**Config:** ${vType} × ${nVar}`);
42
+ }
43
+ if (data["googleDocUrl"] || data["docUrl"]) {
44
+ lines.push(`**Google Doc:** ${data["googleDocUrl"] ?? data["docUrl"]}`);
45
+ }
46
+ if (data["error"]) {
47
+ lines.push(`**Error:** ${data["error"]}`);
48
+ }
49
+ const steps = data["steps"];
50
+ if (steps) {
51
+ lines.push(formatSteps(steps));
52
+ }
53
+ if (Array.isArray(data["hooks"]) && data["hooks"].length > 0) {
54
+ lines.push(`\n**Hooks (${data["hooks"].length}):**`);
55
+ for (const hook of data["hooks"].slice(0, 10)) {
56
+ lines.push(` - ${String(hook)}`);
57
+ }
58
+ }
59
+ if (Array.isArray(data["headlines"]) && data["headlines"].length > 0) {
60
+ lines.push(`\n**Headlines (${data["headlines"].length}):**`);
61
+ for (const h of data["headlines"].slice(0, 10)) {
62
+ lines.push(` - ${String(h)}`);
63
+ }
64
+ }
65
+ if (data["adCopy"] || data["body"]) {
66
+ lines.push(`\n**Ad Copy:**\n${data["adCopy"] ?? data["body"]}`);
67
+ }
68
+ return lines.join("\n");
69
+ }
70
+ export function formatGenesisRun(data) {
71
+ const lines = [];
72
+ const status = data["status"] ?? "unknown";
73
+ lines.push(`## Genesis Run`);
74
+ lines.push(`**Status:** ${status}`);
75
+ if (data["awarenessLevel"] || data["awareness"]) {
76
+ lines.push(`**Awareness Level:** ${data["awarenessLevel"] ?? data["awareness"]}`);
77
+ }
78
+ if (data["inputMethod"] || data["method"]) {
79
+ lines.push(`**Input Method:** ${data["inputMethod"] ?? data["method"]}`);
80
+ }
81
+ if (data["googleDocUrl"] || data["docUrl"]) {
82
+ lines.push(`**Google Doc:** ${data["googleDocUrl"] ?? data["docUrl"]}`);
83
+ }
84
+ if (data["error"]) {
85
+ lines.push(`**Error:** ${data["error"]}`);
86
+ }
87
+ const hookCount = data["hookCount"] ??
88
+ (Array.isArray(data["hooks"]) ? data["hooks"].length : null);
89
+ if (hookCount !== null && hookCount !== undefined) {
90
+ lines.push(`**Hooks:** ${hookCount}`);
91
+ }
92
+ const headlineCount = data["headlineCount"] ??
93
+ (Array.isArray(data["headlines"]) ? data["headlines"].length : null);
94
+ if (headlineCount !== null && headlineCount !== undefined) {
95
+ lines.push(`**Headlines:** ${headlineCount}`);
96
+ }
97
+ const bodyCount = data["bodyCount"] ??
98
+ (Array.isArray(data["bodies"]) ? data["bodies"].length : null);
99
+ if (bodyCount !== null && bodyCount !== undefined) {
100
+ lines.push(`**Body Copies:** ${bodyCount}`);
101
+ }
102
+ if (data["durationMs"]) {
103
+ const secs = Math.round(Number(data["durationMs"]) / 1000);
104
+ lines.push(`**Duration:** ${secs}s`);
105
+ }
106
+ const steps = data["steps"];
107
+ if (steps) {
108
+ lines.push(formatSteps(steps));
109
+ }
110
+ if (data["document"] && !data["googleDocUrl"]) {
111
+ lines.push(`\n---\n**Document Output:**\n${data["document"]}`);
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ export function formatBrowse(generations) {
116
+ if (!Array.isArray(generations) || generations.length === 0) {
117
+ return "No generations found.";
118
+ }
119
+ const lines = [];
120
+ lines.push(`## Generations (${generations.length})`);
121
+ lines.push(`date | agent | status | doc`);
122
+ lines.push(`-----|-------|--------|----`);
123
+ for (const g of generations) {
124
+ if (typeof g !== "object" || g === null)
125
+ continue;
126
+ const gen = g;
127
+ const rawDate = gen["createdAt"] ?? gen["_creationTime"];
128
+ let date = "—";
129
+ if (typeof rawDate === "number") {
130
+ date = new Date(rawDate).toISOString().slice(0, 10);
131
+ }
132
+ else if (typeof rawDate === "string" && rawDate !== "—") {
133
+ const parsed = new Date(rawDate);
134
+ date = isNaN(parsed.getTime()) ? "—" : parsed.toISOString().slice(0, 10);
135
+ }
136
+ const agent = gen["agentName"] ?? gen["agentId"] ?? gen["agent"] ?? gen["pipeline"] ?? "—";
137
+ const status = gen["status"] ?? "—";
138
+ const doc = gen["googleDocUrl"] ?? gen["docUrl"] ?? "—";
139
+ lines.push(`${date} | ${agent} | ${status} | ${doc}`);
140
+ }
141
+ return lines.join("\n");
142
+ }
143
+ export function formatError(res) {
144
+ const lines = [];
145
+ lines.push(`## Error`);
146
+ lines.push(`**HTTP Status:** ${res.status}`);
147
+ if (res.data && typeof res.data === "object") {
148
+ const d = res.data;
149
+ const err = d["error"];
150
+ if (err && typeof err === "object") {
151
+ const e = err;
152
+ if (e["message"])
153
+ lines.push(`**Error:** ${e["message"]}`);
154
+ if (e["code"])
155
+ lines.push(`**Code:** ${e["code"]}`);
156
+ }
157
+ else if (err) {
158
+ lines.push(`**Error:** ${String(err)}`);
159
+ }
160
+ if (d["message"])
161
+ lines.push(`**Message:** ${d["message"]}`);
162
+ if (d["code"] && !(err && typeof err === "object"))
163
+ lines.push(`**Code:** ${d["code"]}`);
164
+ if (!err && !d["message"]) {
165
+ lines.push(`**Details:**\n\`\`\`json\n${JSON.stringify(res.data, null, 2)}\n\`\`\``);
166
+ }
167
+ }
168
+ else if (res.data) {
169
+ lines.push(`**Details:** ${String(res.data)}`);
170
+ }
171
+ return lines.join("\n");
172
+ }
@@ -0,0 +1 @@
1
+ export declare function formatIntelResult(data: Record<string, unknown>): string;
@@ -0,0 +1,38 @@
1
+ export function formatIntelResult(data) {
2
+ const lines = [];
3
+ lines.push("## Intel Analysis Result");
4
+ lines.push(`**Status:** ${data["status"] ?? "unknown"}`);
5
+ if (data["mode"])
6
+ lines.push(`**Mode:** ${data["mode"]}`);
7
+ if (data["phase"] !== undefined)
8
+ lines.push(`**Phase:** ${data["phase"]}`);
9
+ if (data["adsAnalyzed"] !== undefined)
10
+ lines.push(`**Ads Analyzed:** ${data["adsAnalyzed"]}`);
11
+ if (data["winnersFound"] !== undefined)
12
+ lines.push(`**Winners Found:** ${data["winnersFound"]}`);
13
+ if (data["losersFound"] !== undefined)
14
+ lines.push(`**Losers Found:** ${data["losersFound"]}`);
15
+ const attempted = data["combinationsAttempted"];
16
+ const succeeded = data["combinationsSucceeded"];
17
+ const total = data["combinationsCount"];
18
+ if (total !== undefined)
19
+ lines.push(`**Combinations (total):** ${total}`);
20
+ if (attempted !== undefined || succeeded !== undefined) {
21
+ lines.push(`**Phase 3 outputs:** ${succeeded ?? 0} of ${attempted ?? 0} attempted`);
22
+ }
23
+ else if (data["adsWritten"] !== undefined) {
24
+ lines.push(`**Ads Written:** ${data["adsWritten"]}`);
25
+ }
26
+ if (data["phase1DocUrl"])
27
+ lines.push(`**Phase 1 Doc:** ${data["phase1DocUrl"]}`);
28
+ if (data["phase3DocUrl"])
29
+ lines.push(`**Phase 3 Doc:** ${data["phase3DocUrl"]}`);
30
+ if (!data["phase1DocUrl"] && !data["phase3DocUrl"] && data["googleDocUrl"]) {
31
+ lines.push(`**Google Doc:** ${data["googleDocUrl"]}`);
32
+ }
33
+ if (data["googleSheetUrl"])
34
+ lines.push(`**Sheet:** ${data["googleSheetUrl"]}`);
35
+ if (data["error"])
36
+ lines.push(`**Error:** ${data["error"]}`);
37
+ return lines.join("\n");
38
+ }
@@ -0,0 +1,16 @@
1
+ export declare const PROVIDER_ENV_MAP: Record<string, string>;
2
+ export interface RemoteKeys {
3
+ keys: Record<string, string>;
4
+ failed: string[];
5
+ }
6
+ export declare function fetchRemoteKeys(): Promise<RemoteKeys>;
7
+ export declare function resolveEnvFilePath(): string;
8
+ export interface UpsertResult {
9
+ name: string;
10
+ action: "added" | "updated" | "unchanged";
11
+ }
12
+ export declare function upsertEnvVars(filePath: string, vars: Record<string, string>): UpsertResult[];
13
+ export declare function mapKeysToEnvVars(keys: Record<string, string>): {
14
+ vars: Record<string, string>;
15
+ skipped: string[];
16
+ };
@@ -0,0 +1,88 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { apiGetDashboard } from "./client.js";
4
+ export const PROVIDER_ENV_MAP = {
5
+ genesis: "GENESIS_API_KEY",
6
+ anthropic: "ANTHROPIC_API_KEY",
7
+ openrouter: "OPENROUTER_API_KEY",
8
+ elevenlabs: "ELEVENLABS_API_KEY",
9
+ kie: "KIE_API_KEY",
10
+ };
11
+ export async function fetchRemoteKeys() {
12
+ const res = await apiGetDashboard("/api/settings/keys");
13
+ if (!res.ok) {
14
+ const msg = res.data?.error ??
15
+ `failed to fetch keys from dashboard (HTTP ${res.status})`;
16
+ throw new Error(msg);
17
+ }
18
+ return { keys: res.data.keys ?? {}, failed: res.data.failed ?? [] };
19
+ }
20
+ export function resolveEnvFilePath() {
21
+ let dir = process.cwd();
22
+ for (let i = 0; i <= 5; i++) {
23
+ const candidate = path.join(dir, ".env");
24
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
25
+ return candidate;
26
+ }
27
+ const parent = path.dirname(dir);
28
+ if (parent === dir)
29
+ break;
30
+ dir = parent;
31
+ }
32
+ return path.join(process.cwd(), ".env");
33
+ }
34
+ export function upsertEnvVars(filePath, vars) {
35
+ let content = "";
36
+ try {
37
+ content = fs.readFileSync(filePath, "utf8");
38
+ }
39
+ catch {
40
+ content = "";
41
+ }
42
+ const lines = content.length ? content.split(/\r?\n/) : [];
43
+ const results = [];
44
+ for (const [name, value] of Object.entries(vars)) {
45
+ const formatted = `${name}=${formatEnvValue(value)}`;
46
+ const idx = lines.findIndex((l) => {
47
+ const t = l.trim();
48
+ if (!t || t.startsWith("#"))
49
+ return false;
50
+ const eq = t.indexOf("=");
51
+ return eq > 0 && t.slice(0, eq).trim() === name;
52
+ });
53
+ if (idx === -1) {
54
+ lines.push(formatted);
55
+ results.push({ name, action: "added" });
56
+ }
57
+ else if (lines[idx] === formatted) {
58
+ results.push({ name, action: "unchanged" });
59
+ }
60
+ else {
61
+ lines[idx] = formatted;
62
+ results.push({ name, action: "updated" });
63
+ }
64
+ }
65
+ let out = lines.join("\n");
66
+ if (!out.endsWith("\n"))
67
+ out += "\n";
68
+ fs.writeFileSync(filePath, out, { mode: 0o600 });
69
+ return results;
70
+ }
71
+ export function mapKeysToEnvVars(keys) {
72
+ const vars = {};
73
+ const skipped = [];
74
+ for (const [provider, value] of Object.entries(keys)) {
75
+ const envName = PROVIDER_ENV_MAP[provider];
76
+ if (envName)
77
+ vars[envName] = value;
78
+ else
79
+ skipped.push(provider);
80
+ }
81
+ return { vars, skipped };
82
+ }
83
+ function formatEnvValue(value) {
84
+ if (/[\s#'"]/.test(value)) {
85
+ return `"${value.replace(/"/g, '\\"')}"`;
86
+ }
87
+ return value;
88
+ }
@@ -0,0 +1,38 @@
1
+ export declare const BRAND_MARKER_FILE = ".exodus-brand.json";
2
+ export interface BrandDirInfo {
3
+ slug: string;
4
+ name: string;
5
+ dir: string;
6
+ }
7
+ export type Layout = "v2" | "legacy";
8
+ export interface ActiveBrand {
9
+ slug: string | null;
10
+ source: "folder" | "pointer" | null;
11
+ }
12
+ export declare function findParentRoot(startDir?: string): string;
13
+ export declare function detectLayout(root?: string): Layout;
14
+ export declare function listBrandDirs(root?: string): BrandDirInfo[];
15
+ export declare function brandDirNameForSlug(slug: string): string;
16
+ export declare function brandDirFor(root: string, slug: string): string;
17
+ export declare function ensureBrandDir(root: string, brand: {
18
+ slug: string;
19
+ name?: string;
20
+ }): {
21
+ dir: string;
22
+ created: boolean;
23
+ };
24
+ export declare function findBrandDirFromCwd(cwd?: string, root?: string): BrandDirInfo | null;
25
+ export declare function resolveActiveBrand(opts?: {
26
+ cwd?: string;
27
+ root?: string;
28
+ }): ActiveBrand;
29
+ export declare function brandStateDir(opts?: {
30
+ slug?: string;
31
+ cwd?: string;
32
+ root?: string;
33
+ }): string;
34
+ export declare function brandOutputDir(opts?: {
35
+ slug?: string;
36
+ cwd?: string;
37
+ root?: string;
38
+ }): string;
@@ -0,0 +1,153 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export const BRAND_MARKER_FILE = ".exodus-brand.json";
4
+ const RESERVED_DIR_NAMES = new Set([
5
+ "exodus",
6
+ "references",
7
+ "output",
8
+ "state",
9
+ "docs",
10
+ "workspace",
11
+ "node_modules",
12
+ ".claude",
13
+ ".exodus",
14
+ ".backup",
15
+ ]);
16
+ export function findParentRoot(startDir = process.cwd()) {
17
+ let dir = startDir;
18
+ for (let i = 0; i < 8; i++) {
19
+ if (fs.existsSync(path.join(dir, ".env")) ||
20
+ fs.existsSync(path.join(dir, ".exodus"))) {
21
+ return dir;
22
+ }
23
+ const parent = path.dirname(dir);
24
+ if (parent === dir)
25
+ break;
26
+ dir = parent;
27
+ }
28
+ return startDir;
29
+ }
30
+ function readStateFile(root) {
31
+ const p = path.join(root, ".exodus", "state.json");
32
+ if (!fs.existsSync(p))
33
+ return {};
34
+ try {
35
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
36
+ }
37
+ catch {
38
+ return {};
39
+ }
40
+ }
41
+ function readBrandMarker(dir) {
42
+ const p = path.join(dir, BRAND_MARKER_FILE);
43
+ if (!fs.existsSync(p))
44
+ return null;
45
+ try {
46
+ const parsed = JSON.parse(fs.readFileSync(p, "utf-8"));
47
+ if (typeof parsed.slug !== "string" || !parsed.slug.trim())
48
+ return null;
49
+ const slug = parsed.slug.trim();
50
+ const name = typeof parsed.name === "string" && parsed.name.trim()
51
+ ? parsed.name.trim()
52
+ : slug;
53
+ return { slug, name };
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
59
+ export function detectLayout(root = findParentRoot()) {
60
+ if (readStateFile(root).layoutVersion === 2)
61
+ return "v2";
62
+ if (listBrandDirs(root).length > 0)
63
+ return "v2";
64
+ return "legacy";
65
+ }
66
+ export function listBrandDirs(root = findParentRoot()) {
67
+ let entries;
68
+ try {
69
+ entries = fs.readdirSync(root, { withFileTypes: true });
70
+ }
71
+ catch {
72
+ return [];
73
+ }
74
+ const out = [];
75
+ for (const entry of entries) {
76
+ if (!entry.isDirectory())
77
+ continue;
78
+ const dir = path.join(root, entry.name);
79
+ const marker = readBrandMarker(dir);
80
+ if (marker)
81
+ out.push({ ...marker, dir });
82
+ }
83
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
84
+ return out;
85
+ }
86
+ export function brandDirNameForSlug(slug) {
87
+ return RESERVED_DIR_NAMES.has(slug) ? `${slug}-brand` : slug;
88
+ }
89
+ export function brandDirFor(root, slug) {
90
+ const existing = listBrandDirs(root).find((b) => b.slug === slug);
91
+ if (existing)
92
+ return existing.dir;
93
+ return path.join(root, brandDirNameForSlug(slug));
94
+ }
95
+ export function ensureBrandDir(root, brand) {
96
+ const dir = brandDirFor(root, brand.slug);
97
+ const created = !fs.existsSync(path.join(dir, BRAND_MARKER_FILE));
98
+ fs.mkdirSync(path.join(dir, "state"), { recursive: true });
99
+ fs.mkdirSync(path.join(dir, "output"), { recursive: true });
100
+ const marker = { slug: brand.slug, name: brand.name ?? brand.slug };
101
+ const markerPath = path.join(dir, BRAND_MARKER_FILE);
102
+ const existing = readBrandMarker(dir);
103
+ if (!existing || existing.name !== marker.name || existing.slug !== marker.slug) {
104
+ fs.writeFileSync(markerPath, JSON.stringify(marker, null, 2) + "\n", "utf-8");
105
+ }
106
+ return { dir, created };
107
+ }
108
+ export function findBrandDirFromCwd(cwd = process.cwd(), root = findParentRoot(cwd)) {
109
+ let dir = cwd;
110
+ for (let i = 0; i < 16; i++) {
111
+ const marker = readBrandMarker(dir);
112
+ if (marker)
113
+ return { ...marker, dir };
114
+ if (dir === root)
115
+ break;
116
+ const parent = path.dirname(dir);
117
+ if (parent === dir)
118
+ break;
119
+ dir = parent;
120
+ }
121
+ return null;
122
+ }
123
+ export function resolveActiveBrand(opts) {
124
+ const cwd = opts?.cwd ?? process.cwd();
125
+ const root = opts?.root ?? findParentRoot(cwd);
126
+ const fromFolder = findBrandDirFromCwd(cwd, root);
127
+ if (fromFolder)
128
+ return { slug: fromFolder.slug, source: "folder" };
129
+ const pointer = readStateFile(root).activeBrand;
130
+ if (pointer)
131
+ return { slug: pointer, source: "pointer" };
132
+ return { slug: null, source: null };
133
+ }
134
+ export function brandStateDir(opts) {
135
+ const cwd = opts?.cwd ?? process.cwd();
136
+ const root = opts?.root ?? findParentRoot(cwd);
137
+ if (detectLayout(root) === "v2") {
138
+ const slug = opts?.slug ?? resolveActiveBrand({ cwd, root }).slug;
139
+ if (slug)
140
+ return path.join(brandDirFor(root, slug), "state");
141
+ }
142
+ return path.join(root, "state");
143
+ }
144
+ export function brandOutputDir(opts) {
145
+ const cwd = opts?.cwd ?? process.cwd();
146
+ const root = opts?.root ?? findParentRoot(cwd);
147
+ if (detectLayout(root) === "v2") {
148
+ const slug = opts?.slug ?? resolveActiveBrand({ cwd, root }).slug;
149
+ if (slug)
150
+ return path.join(brandDirFor(root, slug), "output");
151
+ }
152
+ return path.join(root, "output");
153
+ }
@@ -0,0 +1 @@
1
+ export declare function loadWorkspaceEnv(startDir?: string): void;
@@ -0,0 +1,47 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export function loadWorkspaceEnv(startDir = process.cwd()) {
4
+ const envPath = findEnvFile(startDir, 8);
5
+ if (!envPath)
6
+ return;
7
+ let contents;
8
+ try {
9
+ contents = fs.readFileSync(envPath, "utf8");
10
+ }
11
+ catch {
12
+ return;
13
+ }
14
+ for (const rawLine of contents.split(/\r?\n/)) {
15
+ const line = rawLine.trim();
16
+ if (!line || line.startsWith("#"))
17
+ continue;
18
+ const eq = line.indexOf("=");
19
+ if (eq <= 0)
20
+ continue;
21
+ const key = line.slice(0, eq).trim();
22
+ if (!key || /[^A-Za-z0-9_]/.test(key))
23
+ continue;
24
+ if (process.env[key] !== undefined)
25
+ continue;
26
+ let value = line.slice(eq + 1).trim();
27
+ if ((value.startsWith('"') && value.endsWith('"')) ||
28
+ (value.startsWith("'") && value.endsWith("'"))) {
29
+ value = value.slice(1, -1);
30
+ }
31
+ process.env[key] = value;
32
+ }
33
+ }
34
+ function findEnvFile(startDir, maxLevels) {
35
+ let dir = startDir;
36
+ for (let i = 0; i <= maxLevels; i++) {
37
+ const candidate = path.join(dir, ".env");
38
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
39
+ return candidate;
40
+ }
41
+ const parent = path.dirname(dir);
42
+ if (parent === dir)
43
+ break;
44
+ dir = parent;
45
+ }
46
+ return null;
47
+ }
@@ -0,0 +1,54 @@
1
+ export type PixarResumeStage = "scriptPrep" | "parser" | "storyboard" | "castSheet" | "storyboardImages" | "voiceAndClips" | "stitch";
2
+ export interface PixarRunPayload {
3
+ runId: string;
4
+ resumeFrom?: PixarResumeStage;
5
+ }
6
+ export interface PixarPausedResult {
7
+ paused: true;
8
+ status: string;
9
+ }
10
+ export interface PixarCompletedResult {
11
+ muxPlaybackId: string;
12
+ muxAssetId: string;
13
+ finalDurationSec: number;
14
+ }
15
+ export type PixarRunResult = PixarPausedResult | PixarCompletedResult;
16
+ export interface PixarRunRow {
17
+ _id: string;
18
+ status: string;
19
+ failedStage?: string;
20
+ errorMessage?: string;
21
+ muxPlaybackId?: string;
22
+ muxAssetId?: string;
23
+ finalDurationSec?: number;
24
+ cost?: {
25
+ total?: number;
26
+ };
27
+ castSheetStorageId?: string;
28
+ voiceoverStorageIds?: string[];
29
+ silentClipStorageIds?: string[];
30
+ googleDocUrl?: string;
31
+ }
32
+ export interface PixarRunListItem {
33
+ _id: string;
34
+ status: string;
35
+ createdAt: number;
36
+ scriptInputMode?: "provided" | "generated";
37
+ finalDurationSec?: number;
38
+ muxPlaybackId?: string;
39
+ failedStage?: string;
40
+ }
41
+ export interface PixarBundle {
42
+ runPixarPipeline(payload: PixarRunPayload): Promise<PixarRunResult>;
43
+ resolveWorkspaceId(workspaceSlug: string): Promise<string | null>;
44
+ createRun(args: {
45
+ workspaceId: string;
46
+ triggeredByUserId: string;
47
+ script?: string;
48
+ scriptPrepBrief?: string;
49
+ refImageUrl?: string;
50
+ reviewMode: "auto" | "review";
51
+ }): Promise<string>;
52
+ fetchRun(runId: string): Promise<PixarRunRow | null>;
53
+ listRuns(workspaceId: string, limit?: number): Promise<PixarRunListItem[]>;
54
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ export interface PollOptions {
2
+ path: string;
3
+ intervalMs?: number;
4
+ timeoutMs?: number;
5
+ terminalStatuses?: string[];
6
+ onProgress?: (data: Record<string, unknown>) => void;
7
+ isDone?: (data: Record<string, unknown>) => boolean;
8
+ }
9
+ export interface PollResult {
10
+ ok: boolean;
11
+ data: Record<string, unknown>;
12
+ timedOut: boolean;
13
+ }
14
+ export declare function pollUntilDone(opts: PollOptions): Promise<PollResult>;
@@ -0,0 +1,32 @@
1
+ import { apiGet } from "./client.js";
2
+ const DEFAULT_TERMINAL_STATUSES = ["completed", "failed"];
3
+ function delay(ms) {
4
+ return new Promise((resolve) => setTimeout(resolve, ms));
5
+ }
6
+ export async function pollUntilDone(opts) {
7
+ const { path, intervalMs = 3000, timeoutMs = 600_000, terminalStatuses, onProgress, isDone, } = opts;
8
+ const terminal = new Set([...DEFAULT_TERMINAL_STATUSES, ...(terminalStatuses ?? [])]);
9
+ const deadline = Date.now() + timeoutMs;
10
+ while (true) {
11
+ const res = await apiGet(path);
12
+ if (onProgress) {
13
+ onProgress(res.data);
14
+ }
15
+ const status = typeof res.data["status"] === "string" ? res.data["status"] : undefined;
16
+ if (!res.ok) {
17
+ return { ok: false, data: res.data, timedOut: false };
18
+ }
19
+ if (status === "failed") {
20
+ return { ok: false, data: res.data, timedOut: false };
21
+ }
22
+ const statusTerminal = status ? terminal.has(status) : false;
23
+ const customDone = isDone ? isDone(res.data) : true;
24
+ if (statusTerminal && customDone) {
25
+ return { ok: true, data: res.data, timedOut: false };
26
+ }
27
+ if (Date.now() + intervalMs > deadline) {
28
+ return { ok: false, data: res.data, timedOut: true };
29
+ }
30
+ await delay(intervalMs);
31
+ }
32
+ }
@@ -0,0 +1,18 @@
1
+ export declare function hasBinary(name: string): boolean;
2
+ export interface KeySyncResult {
3
+ pulled: string[];
4
+ failed: string[];
5
+ error?: string;
6
+ }
7
+ export declare function syncKeysToEnv(): Promise<KeySyncResult>;
8
+ export interface RequiredKey {
9
+ label: string;
10
+ anyOf: string[];
11
+ }
12
+ export declare const PIXAR_REQUIRED_KEYS: RequiredKey[];
13
+ export declare const PIXAR_MUX_ENV_VARS: string[];
14
+ type Env = Record<string, string | undefined>;
15
+ export declare function missingRequiredKeys(env: Env, required?: RequiredKey[]): string[];
16
+ export declare function missingRequiredEnvVars(env: Env, required?: RequiredKey[]): string[];
17
+ export declare function muxConfigured(env: Env): boolean;
18
+ export {};