roughbook 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.
Files changed (144) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +166 -2
  3. package/dist/agents/claude.js +214 -0
  4. package/dist/agents/claude.js.map +1 -0
  5. package/dist/agents/codex.js +179 -0
  6. package/dist/agents/codex.js.map +1 -0
  7. package/dist/agents/command.js +47 -0
  8. package/dist/agents/command.js.map +1 -0
  9. package/dist/agents/index.js +21 -0
  10. package/dist/agents/index.js.map +1 -0
  11. package/dist/agents/process.js +76 -0
  12. package/dist/agents/process.js.map +1 -0
  13. package/dist/agents/session.js +26 -0
  14. package/dist/agents/session.js.map +1 -0
  15. package/dist/agents/types.js +2 -0
  16. package/dist/agents/types.js.map +1 -0
  17. package/dist/api/client.js +138 -0
  18. package/dist/api/client.js.map +1 -0
  19. package/dist/api/errors.js +40 -0
  20. package/dist/api/errors.js.map +1 -0
  21. package/dist/api/outbox.js +105 -0
  22. package/dist/api/outbox.js.map +1 -0
  23. package/dist/api/types.js +4 -0
  24. package/dist/api/types.js.map +1 -0
  25. package/dist/cli.js +10 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/commands/agents.js +39 -0
  28. package/dist/commands/agents.js.map +1 -0
  29. package/dist/commands/context.js +27 -0
  30. package/dist/commands/context.js.map +1 -0
  31. package/dist/commands/done.js +125 -0
  32. package/dist/commands/done.js.map +1 -0
  33. package/dist/commands/index.js +101 -0
  34. package/dist/commands/index.js.map +1 -0
  35. package/dist/commands/init.js +203 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/login.js +93 -0
  38. package/dist/commands/login.js.map +1 -0
  39. package/dist/commands/logout.js +34 -0
  40. package/dist/commands/logout.js.map +1 -0
  41. package/dist/commands/promote.js +105 -0
  42. package/dist/commands/promote.js.map +1 -0
  43. package/dist/commands/pull.js +45 -0
  44. package/dist/commands/pull.js.map +1 -0
  45. package/dist/commands/revert.js +39 -0
  46. package/dist/commands/revert.js.map +1 -0
  47. package/dist/commands/review.js +73 -0
  48. package/dist/commands/review.js.map +1 -0
  49. package/dist/commands/run.js +147 -0
  50. package/dist/commands/run.js.map +1 -0
  51. package/dist/commands/shared.js +81 -0
  52. package/dist/commands/shared.js.map +1 -0
  53. package/dist/commands/start.js +119 -0
  54. package/dist/commands/start.js.map +1 -0
  55. package/dist/commands/status.js +62 -0
  56. package/dist/commands/status.js.map +1 -0
  57. package/dist/config/credentials.js +17 -0
  58. package/dist/config/credentials.js.map +1 -0
  59. package/dist/config/server.js +29 -0
  60. package/dist/config/server.js.map +1 -0
  61. package/dist/config/settings.js +21 -0
  62. package/dist/config/settings.js.map +1 -0
  63. package/dist/config/userDir.js +18 -0
  64. package/dist/config/userDir.js.map +1 -0
  65. package/dist/fs/json.js +44 -0
  66. package/dist/fs/json.js.map +1 -0
  67. package/dist/fs/lock.js +38 -0
  68. package/dist/fs/lock.js.map +1 -0
  69. package/dist/io.js +26 -0
  70. package/dist/io.js.map +1 -0
  71. package/dist/launch/open.js +48 -0
  72. package/dist/launch/open.js.map +1 -0
  73. package/dist/launch/platform.js +38 -0
  74. package/dist/launch/platform.js.map +1 -0
  75. package/dist/launch/pty.js +61 -0
  76. package/dist/launch/pty.js.map +1 -0
  77. package/dist/launch/terminal.js +194 -0
  78. package/dist/launch/terminal.js.map +1 -0
  79. package/dist/launch/which.js +59 -0
  80. package/dist/launch/which.js.map +1 -0
  81. package/dist/repo/diff.js +26 -0
  82. package/dist/repo/diff.js.map +1 -0
  83. package/dist/repo/framework.js +41 -0
  84. package/dist/repo/framework.js.map +1 -0
  85. package/dist/repo/link.js +28 -0
  86. package/dist/repo/link.js.map +1 -0
  87. package/dist/repo/prep.js +447 -0
  88. package/dist/repo/prep.js.map +1 -0
  89. package/dist/repo/preview.js +144 -0
  90. package/dist/repo/preview.js.map +1 -0
  91. package/dist/repo/root.js +48 -0
  92. package/dist/repo/root.js.map +1 -0
  93. package/dist/repo/scratch.js +111 -0
  94. package/dist/repo/scratch.js.map +1 -0
  95. package/dist/runs/checks.js +160 -0
  96. package/dist/runs/checks.js.map +1 -0
  97. package/dist/runs/lifecycle.js +142 -0
  98. package/dist/runs/lifecycle.js.map +1 -0
  99. package/dist/runs/promote.js +185 -0
  100. package/dist/runs/promote.js.map +1 -0
  101. package/dist/runs/pull.js +182 -0
  102. package/dist/runs/pull.js.map +1 -0
  103. package/dist/runs/reviews.js +34 -0
  104. package/dist/runs/reviews.js.map +1 -0
  105. package/dist/runs/slots.js +96 -0
  106. package/dist/runs/slots.js.map +1 -0
  107. package/dist/runs/snapshot.js +265 -0
  108. package/dist/runs/snapshot.js.map +1 -0
  109. package/dist/runs/store.js +38 -0
  110. package/dist/runs/store.js.map +1 -0
  111. package/dist/runs/types.js +3 -0
  112. package/dist/runs/types.js.map +1 -0
  113. package/dist/runs/watch.js +42 -0
  114. package/dist/runs/watch.js.map +1 -0
  115. package/dist/screenshot/browser.js +163 -0
  116. package/dist/screenshot/browser.js.map +1 -0
  117. package/dist/screenshot/iteration.js +37 -0
  118. package/dist/screenshot/iteration.js.map +1 -0
  119. package/dist/server/app.js +321 -0
  120. package/dist/server/app.js.map +1 -0
  121. package/dist/server/security.js +44 -0
  122. package/dist/server/security.js.map +1 -0
  123. package/dist/server/studio.js +618 -0
  124. package/dist/server/studio.js.map +1 -0
  125. package/dist/server/terminals.js +142 -0
  126. package/dist/server/terminals.js.map +1 -0
  127. package/dist/ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  128. package/dist/ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  129. package/dist/ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  130. package/dist/ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  131. package/dist/ui/assets/index-D-TSXfvz.css +1 -0
  132. package/dist/ui/assets/index-HGV8FKwI.js +27 -0
  133. package/dist/ui/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  134. package/dist/ui/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  135. package/dist/ui/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  136. package/dist/ui/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  137. package/dist/ui/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
  138. package/dist/ui/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  139. package/dist/ui/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  140. package/dist/ui/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  141. package/dist/ui/index.html +14 -0
  142. package/dist/version.js +4 -0
  143. package/dist/version.js.map +1 -0
  144. package/package.json +68 -4
@@ -0,0 +1,48 @@
1
+ import { existsSync } from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ /** The studio's folder in the client's repo. */
5
+ export const ROUGH_DIR = 'rough';
6
+ /** Inside `rough/`: which organization and project the repo works on. Committed with `rough/`. */
7
+ export const LINK_FILE = 'roughbook.json';
8
+ /** Inside `rough/`: runs, snapshots and the outbox. Never committed. */
9
+ export const STATE_DIR = '.roughbook';
10
+ /** Whether `dir` is the home folder or one of its ancestors: never a repo the studio works in. */
11
+ export function isHomeOrAbove(dir, home = os.homedir()) {
12
+ const inside = path.relative(path.resolve(dir), home);
13
+ return inside === '' || (!inside.startsWith('..') && !path.isAbsolute(inside));
14
+ }
15
+ /**
16
+ * Walks up from `start` to the first folder `found` accepts. Never past the top of a git repo,
17
+ * and never into the home folder or above it, so a stray file there cannot capture every folder
18
+ * beneath it.
19
+ */
20
+ export function searchUp(start, found, home = os.homedir()) {
21
+ let dir = path.resolve(start);
22
+ for (;;) {
23
+ if (isHomeOrAbove(dir, home))
24
+ return null;
25
+ if (found(dir))
26
+ return dir;
27
+ if (existsSync(path.join(dir, '.git')))
28
+ return null;
29
+ const parent = path.dirname(dir);
30
+ if (parent === dir)
31
+ return null;
32
+ dir = parent;
33
+ }
34
+ }
35
+ /**
36
+ * The repo the studio works in: the nearest folder, from `start` upwards, that has
37
+ * `rough/roughbook.json`. Null outside one.
38
+ */
39
+ export function findRepoRoot(start, home) {
40
+ return searchUp(start, dir => existsSync(path.join(dir, ROUGH_DIR, LINK_FILE)), home);
41
+ }
42
+ export function roughPath(repoRoot, ...parts) {
43
+ return path.join(repoRoot, ROUGH_DIR, ...parts);
44
+ }
45
+ export function statePath(repoRoot, ...parts) {
46
+ return path.join(repoRoot, ROUGH_DIR, STATE_DIR, ...parts);
47
+ }
48
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/repo/root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,gDAAgD;AAChD,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC;AACjC,kGAAkG;AAClG,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAC1C,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,YAAY,CAAC;AAEtC,kGAAkG;AAClG,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,OAAe,EAAE,CAAC,OAAO,EAAE;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IACtD,OAAO,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAa,EACb,KAA+B,EAC/B,OAAe,EAAE,CAAC,OAAO,EAAE;IAE3B,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,SAAS,CAAC;QACR,IAAI,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,KAAK,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QAC3B,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,IAAa;IACvD,OAAO,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,GAAG,KAAe;IAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,GAAG,KAAe;IAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,111 @@
1
+ // `roughbook init --scratch <dir>`: a minimal Next.js 16 app with Tailwind v4, for a project that
2
+ // has no repo yet, following Next.js's manual installation. Nothing is installed here; the
3
+ // designer runs npm install.
4
+ import { execFile } from 'node:child_process';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ const PACKAGE_NAME = /^[a-z0-9][a-z0-9-]*$/;
8
+ export function scratchFiles(name) {
9
+ const pkg = PACKAGE_NAME.test(name) ? name : 'scratch-site';
10
+ return {
11
+ 'package.json': `${JSON.stringify({
12
+ name: pkg,
13
+ version: '0.1.0',
14
+ private: true,
15
+ scripts: { dev: 'next dev', build: 'next build', start: 'next start' },
16
+ dependencies: { next: '^16.3.8', react: '^19.3.0', 'react-dom': '^19.3.0' },
17
+ devDependencies: {
18
+ '@tailwindcss/postcss': '^4.3.3',
19
+ '@types/node': '^24.19.1',
20
+ '@types/react': '^19.3.0',
21
+ '@types/react-dom': '^19.3.0',
22
+ tailwindcss: '^4.3.3',
23
+ typescript: '^5.9.3'
24
+ }
25
+ }, null, 2)}\n`,
26
+ 'tsconfig.json': `${JSON.stringify({
27
+ compilerOptions: {
28
+ target: 'ES2017',
29
+ lib: ['dom', 'dom.iterable', 'esnext'],
30
+ allowJs: true,
31
+ skipLibCheck: true,
32
+ strict: true,
33
+ noEmit: true,
34
+ esModuleInterop: true,
35
+ module: 'esnext',
36
+ moduleResolution: 'bundler',
37
+ resolveJsonModule: true,
38
+ isolatedModules: true,
39
+ jsx: 'react-jsx',
40
+ incremental: true,
41
+ plugins: [{ name: 'next' }],
42
+ paths: { '@/*': ['./src/*'] }
43
+ },
44
+ include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
45
+ exclude: ['node_modules']
46
+ }, null, 2)}\n`,
47
+ 'next.config.ts': `import type { NextConfig } from "next";
48
+
49
+ const nextConfig: NextConfig = {};
50
+
51
+ export default nextConfig;
52
+ `,
53
+ 'postcss.config.mjs': `const config = {
54
+ plugins: {
55
+ "@tailwindcss/postcss": {},
56
+ },
57
+ };
58
+
59
+ export default config;
60
+ `,
61
+ '.gitignore': `/node_modules
62
+ /.next/
63
+ /out/
64
+ next-env.d.ts
65
+ *.tsbuildinfo
66
+ .env*.local
67
+ `,
68
+ 'src/app/globals.css': `@import "tailwindcss";
69
+ `,
70
+ 'src/app/layout.tsx': `import type { ReactNode } from "react";
71
+ import "./globals.css";
72
+
73
+ export default function RootLayout({ children }: { children: ReactNode }) {
74
+ return (
75
+ <html lang="en">
76
+ <body>{children}</body>
77
+ </html>
78
+ );
79
+ }
80
+ `,
81
+ 'src/app/page.tsx': `export default function Page() {
82
+ return <main className="p-8">Designs live in rough/. Run roughbook start to see them.</main>;
83
+ }
84
+ `
85
+ };
86
+ }
87
+ /** Why a scratch app cannot be made in `dir`, or null. */
88
+ export function checkScratchDir(dir) {
89
+ if (!fs.existsSync(dir))
90
+ return null;
91
+ if (!fs.statSync(dir).isDirectory())
92
+ return `${dir} is a file.`;
93
+ return fs.readdirSync(dir).length ? `${dir} is not empty.` : null;
94
+ }
95
+ export function writeScratch(dir) {
96
+ const problem = checkScratchDir(dir);
97
+ if (problem)
98
+ throw new Error(problem);
99
+ for (const [file, content] of Object.entries(scratchFiles(path.basename(dir).toLowerCase()))) {
100
+ const target = path.join(dir, ...file.split('/'));
101
+ fs.mkdirSync(path.dirname(target), { recursive: true });
102
+ fs.writeFileSync(target, content);
103
+ }
104
+ }
105
+ /** `git init`, so rough/ has a record from the start. False when git is not installed. */
106
+ export function gitInit(dir) {
107
+ return new Promise(resolve => {
108
+ execFile('git', ['init', '--quiet'], { cwd: dir, windowsHide: true, timeout: 30_000 }, error => resolve(!error));
109
+ });
110
+ }
111
+ //# sourceMappingURL=scratch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch.js","sourceRoot":"","sources":["../../src/repo/scratch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ // The backend decides which checks a run's output must pass and sends them with the run; the
2
+ // studio applies them here, to the files on this machine. Ported from the backend's
3
+ // utils/compile/checks.ts (itself from roughbook runner/validate.ts, v2-local), with a code on
4
+ // every finding so the backend records codes, never sentences.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { iterationDir } from '../repo/preview.js';
8
+ import { ROUGH_DIR } from '../repo/root.js';
9
+ import { isPlaceholder } from './slots.js';
10
+ /** The files an iteration's checks read: its own top-level sources, not meta.json. */
11
+ const SOURCE = /\.(tsx?|jsx?|mjs|cjs|css|json|md|svg)$/i;
12
+ const CODE = /\.(tsx?|jsx?|mjs|cjs)$/i;
13
+ /** The top-level source files of a folder, or null when it does not exist. */
14
+ export function readFolder(dir) {
15
+ if (!fs.existsSync(dir))
16
+ return null;
17
+ const files = new Map();
18
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
19
+ if (!entry.isFile() || entry.name === 'meta.json' || !SOURCE.test(entry.name))
20
+ continue;
21
+ files.set(entry.name, fs.readFileSync(path.join(dir, entry.name), 'utf8'));
22
+ }
23
+ return files;
24
+ }
25
+ /** Letters and digits only, lowercased, single-spaced: what a reader sees. */
26
+ function normalizeForMatch(text) {
27
+ return text
28
+ .toLowerCase()
29
+ .replace(/&nbsp;|&#160;/g, ' ')
30
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
31
+ .trim();
32
+ }
33
+ /**
34
+ * Whether the headline can be found in the source, with JSX tags and string fragments stripped,
35
+ * so a headline split across lines or wrapped in spans still counts. A miss is only a warning.
36
+ */
37
+ export function headlineAppearsIn(source, headline) {
38
+ const wanted = normalizeForMatch(headline);
39
+ if (!wanted)
40
+ return true;
41
+ const visible = source
42
+ .replace(/<[^<>]*>/g, ' ')
43
+ .replace(/\{\s*["'`]\s*["'`]\s*\}/g, ' ')
44
+ .replace(/["'`{}]/g, ' ');
45
+ return normalizeForMatch(visible).includes(wanted);
46
+ }
47
+ export function applyChecks(checks, subject) {
48
+ const files = subject.files;
49
+ if (!files) {
50
+ return {
51
+ failure: { code: 'TARGET_MISSING', message: 'The iteration folder does not exist.' },
52
+ warnings: []
53
+ };
54
+ }
55
+ for (const check of checks) {
56
+ if (check.kind !== 'entry-file')
57
+ continue;
58
+ const entry = files.get(check.file);
59
+ if (entry == null) {
60
+ return {
61
+ failure: { code: 'ENTRY_MISSING', message: `${check.file} was not created.` },
62
+ warnings: []
63
+ };
64
+ }
65
+ if (entry.length === 0) {
66
+ return { failure: { code: 'ENTRY_EMPTY', message: `${check.file} is empty.` }, warnings: [] };
67
+ }
68
+ if (isPlaceholder(entry)) {
69
+ return {
70
+ failure: {
71
+ code: 'PLACEHOLDER',
72
+ message: `${check.file} is still the placeholder. Is the session finished?`
73
+ },
74
+ warnings: []
75
+ };
76
+ }
77
+ }
78
+ const warnings = [];
79
+ for (const check of checks) {
80
+ if (check.kind === 'changed-from-parent')
81
+ warnings.push(...parentFindings(check.parent, subject));
82
+ }
83
+ const source = [...files.values()].join('\n');
84
+ for (const check of checks) {
85
+ if (check.kind === 'approved-headline' && !headlineAppearsIn(source, check.headline)) {
86
+ warnings.push({
87
+ code: 'APPROVED_HEADLINE_MISSING',
88
+ message: `The approved headline (Version ${check.version}) does not appear in the source: "${check.headline}"`
89
+ });
90
+ }
91
+ }
92
+ return { failure: null, warnings };
93
+ }
94
+ /** A child starts as a copy of its parent, so "index.tsx exists" proves nothing about it. */
95
+ function parentFindings(parentId, subject) {
96
+ const findings = [];
97
+ const own = subject.files;
98
+ const foreign = new Set();
99
+ for (const text of own.values()) {
100
+ for (const match of text.matchAll(/iteration-\d{2}/g)) {
101
+ if (match[0] !== subject.iterationId)
102
+ foreign.add(match[0]);
103
+ }
104
+ }
105
+ if (foreign.has(parentId)) {
106
+ findings.push({
107
+ code: 'NAMES_PARENT',
108
+ message: `The source still names ${parentId} as itself; ${subject.iterationId} should show its own identity.`
109
+ });
110
+ }
111
+ const parent = subject.parentFiles;
112
+ if (parent) {
113
+ const same = own.size === parent.size && [...own].every(([name, text]) => parent.get(name) === text);
114
+ if (same) {
115
+ findings.push({
116
+ code: 'UNCHANGED_FROM_PARENT',
117
+ message: `Every file is identical to ${parentId}. The child was not changed from its parent.`
118
+ });
119
+ }
120
+ }
121
+ return findings;
122
+ }
123
+ const IMPORT = /(?:\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|^\s*import\s+)["'`]([^"'`]+)["'`]/gm;
124
+ /**
125
+ * Files outside rough/ that import from it: the app must never depend on a design reference. The
126
+ * preview route is the one place allowed to, and only in development.
127
+ */
128
+ export function importsFromRough(repoRoot, files, previewRoute) {
129
+ const offenders = [];
130
+ for (const file of files) {
131
+ if (!CODE.test(file) || file.startsWith(`${ROUGH_DIR}/`))
132
+ continue;
133
+ if (previewRoute && file.startsWith(`${previewRoute}/`))
134
+ continue;
135
+ let text;
136
+ try {
137
+ text = fs.readFileSync(path.join(repoRoot, ...file.split('/')), 'utf8');
138
+ }
139
+ catch {
140
+ continue;
141
+ }
142
+ for (const match of text.matchAll(IMPORT)) {
143
+ if (match[1].split('/').includes(ROUGH_DIR)) {
144
+ offenders.push(file);
145
+ break;
146
+ }
147
+ }
148
+ }
149
+ return offenders;
150
+ }
151
+ /** The backend's checks for one run, read from its folder and its parent's. */
152
+ export function checkIteration(repoRoot, run) {
153
+ const dir = (id) => path.join(repoRoot, ...iterationDir(run.page, run.component, id).split('/'));
154
+ return applyChecks(run.checks, {
155
+ iterationId: run.iteration,
156
+ files: readFolder(dir(run.iteration)),
157
+ parentFiles: run.parent ? readFolder(dir(run.parent)) : null
158
+ });
159
+ }
160
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/runs/checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ import { ApiError } from '../api/errors.js';
2
+ import { localOrigin } from '../config/settings.js';
3
+ import { REGISTRY_FILE, iterationDir, previewRouteDir, writePreviewRegistry } from '../repo/preview.js';
4
+ import { ROUGH_DIR } from '../repo/root.js';
5
+ import { checkIteration, importsFromRough } from './checks.js';
6
+ import { createSlot, iterationExists, promptFile, writePrompt, assertTarget } from './slots.js';
7
+ import { allChanged, compareFiles, hashRepo, readSnapshot, takeSnapshot } from './snapshot.js';
8
+ import { listRuns, readRun, saveRun } from './store.js';
9
+ /** The files a run may write: its folder, its prompt, and the registry the studio regenerates. */
10
+ export function inScope(file, runs) {
11
+ if (file === `${ROUGH_DIR}/${REGISTRY_FILE}`)
12
+ return true;
13
+ return runs.some(run => file.startsWith(`${run.targetFolder}/`) || file === run.promptFile);
14
+ }
15
+ /** Whether two runs were going at the same time, so each may write the other's folder. */
16
+ function overlapped(a, b, now) {
17
+ const end = (run) => run.finishedAt ?? now.toISOString();
18
+ return a.startedAt <= end(b) && b.startedAt <= end(a);
19
+ }
20
+ /** The stand-in the backend writes where the asset files' origin belongs. */
21
+ export const ASSET_ORIGIN_PLACEHOLDER = '{{roughbook:asset-origin}}';
22
+ export async function startRuns(repoRoot, api, link, request, options = {}) {
23
+ const now = options.now ?? (() => new Date());
24
+ const assetOrigin = options.assetOrigin ?? localOrigin({ localPort: null });
25
+ assertTarget(request.page, request.component, 'iteration-01');
26
+ if (request.kind !== 'generate') {
27
+ assertTarget(request.page, request.component, request.parent);
28
+ if (!iterationExists(repoRoot, request.page, request.component, request.parent)) {
29
+ throw new Error(`${iterationDir(request.page, request.component, request.parent)} is not in this repo, so it cannot be ${request.kind === 'fork' ? 'forked' : 'refined'} here.`);
30
+ }
31
+ }
32
+ const started = await api.startRuns(link.orgId, link.project, request);
33
+ for (const run of started) {
34
+ assertTarget(request.page, request.component, run.iteration);
35
+ // The folder is the studio's to decide; one the server names elsewhere is refused.
36
+ if (run.targetFolder !== iterationDir(request.page, request.component, run.iteration)) {
37
+ throw new Error(`The server asked to write ${run.targetFolder}, which is not an iteration folder.`);
38
+ }
39
+ }
40
+ // Before the slots, so writing them counts as the runs' own change.
41
+ await takeSnapshot(repoRoot, started.map(run => run.runId));
42
+ const records = [];
43
+ for (const run of started) {
44
+ const record = {
45
+ runId: run.runId,
46
+ orgId: link.orgId,
47
+ project: link.project,
48
+ page: request.page,
49
+ component: request.component,
50
+ iteration: run.iteration,
51
+ parent: run.parent,
52
+ kind: request.kind,
53
+ agent: request.agent,
54
+ mode: request.mode,
55
+ targetFolder: run.targetFolder,
56
+ promptFile: promptFile(request.page, request.component, run.runId),
57
+ checks: run.checks,
58
+ startedAt: now().toISOString(),
59
+ status: 'started',
60
+ finishedAt: null,
61
+ outcome: null,
62
+ failure: null,
63
+ warnings: [],
64
+ outOfScope: [],
65
+ reported: null
66
+ };
67
+ createSlot(repoRoot, record);
68
+ const standIn = run.placeholders?.assetOrigin ?? ASSET_ORIGIN_PLACEHOLDER;
69
+ writePrompt(repoRoot, record.promptFile, run.prompt.replaceAll(standIn, () => assetOrigin));
70
+ await saveRun(repoRoot, record);
71
+ records.push(record);
72
+ }
73
+ writePreviewRegistry(repoRoot);
74
+ return records;
75
+ }
76
+ export async function finishRun(repoRoot, api, outbox, link, runId, options = {}) {
77
+ const run = await readRun(repoRoot, runId);
78
+ if (!run)
79
+ throw new Error(`There is no run ${runId} in this repo.`);
80
+ if (run.status === 'finished')
81
+ return run;
82
+ const now = options.now ?? new Date();
83
+ const checked = checkIteration(repoRoot, run);
84
+ const after = await hashRepo(repoRoot);
85
+ const snapshot = await readSnapshot(repoRoot, runId);
86
+ const others = (await listRuns(repoRoot)).filter(other => other.runId !== run.runId && overlapped(other, run, now));
87
+ const outOfScope = snapshot
88
+ ? allChanged(compareFiles(snapshot.files, after.files)).filter(file => !inScope(file, [run, ...others]))
89
+ : [];
90
+ const offenders = importsFromRough(repoRoot, Object.keys(after.files), link.appDir ? previewRouteDir(link.appDir) : null);
91
+ const warnings = [...checked.warnings];
92
+ if (outOfScope.length) {
93
+ warnings.push({
94
+ code: 'OUT_OF_SCOPE_WRITE',
95
+ message: `${outOfScope.length} file${outOfScope.length === 1 ? '' : 's'} changed outside ${run.targetFolder}: ${outOfScope.slice(0, 5).join(', ')}${outOfScope.length > 5 ? ', …' : ''}`
96
+ });
97
+ }
98
+ if (offenders.length) {
99
+ warnings.push({
100
+ code: 'IMPORTS_FROM_ROUGH',
101
+ message: `${offenders.join(', ')} import${offenders.length === 1 ? 's' : ''} from ${ROUGH_DIR}/. The app must never depend on a design reference.`
102
+ });
103
+ }
104
+ const outcome = options.outcome ??
105
+ (checked.failure ? 'failed' : warnings.length ? 'completed_with_warnings' : 'completed');
106
+ const codes = [
107
+ ...new Set([
108
+ ...(checked.failure ? [checked.failure.code] : []),
109
+ ...warnings.map(item => item.code)
110
+ ])
111
+ ];
112
+ let reported;
113
+ try {
114
+ const sent = await outbox.send(api, `/orgs/${run.orgId}/runs/${run.runId}/finish`, {
115
+ outcome,
116
+ durationMs: Math.max(0, now.getTime() - Date.parse(run.startedAt)),
117
+ warnings: codes,
118
+ outOfScopeWrites: outOfScope.length,
119
+ usage: options.usage ?? null
120
+ });
121
+ reported = sent.status;
122
+ }
123
+ catch (error) {
124
+ if (!(error instanceof ApiError))
125
+ throw error;
126
+ reported = null;
127
+ }
128
+ const finished = {
129
+ ...run,
130
+ status: 'finished',
131
+ finishedAt: now.toISOString(),
132
+ outcome,
133
+ failure: checked.failure,
134
+ warnings,
135
+ outOfScope,
136
+ reported
137
+ };
138
+ await saveRun(repoRoot, finished);
139
+ writePreviewRegistry(repoRoot);
140
+ return finished;
141
+ }
142
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/runs/lifecycle.ts"],"names":[],"mappings":"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"}