@12ui/design 0.2.76 → 0.2.77
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/SKILL.md +1 -1
- package/dist/cli-codex-readiness.d.ts +6 -0
- package/dist/cli-codex-readiness.d.ts.map +1 -1
- package/dist/cli-codex-readiness.js +12 -12
- package/dist/cli-codex-readiness.js.map +1 -1
- package/dist/cli-help/design.d.ts +2 -2
- package/dist/cli-help/design.d.ts.map +1 -1
- package/dist/cli-help/design.js +2 -0
- package/dist/cli-help/design.js.map +1 -1
- package/dist/cli-help/index.d.ts +2 -2
- package/dist/cli-review-command.d.ts.map +1 -1
- package/dist/cli-review-command.js +2 -0
- package/dist/cli-review-command.js.map +1 -1
- package/dist/cli-run.d.ts.map +1 -1
- package/dist/cli-run.js +6 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/cli-workspace-draw.d.ts +1 -11
- package/dist/cli-workspace-draw.d.ts.map +1 -1
- package/dist/cli-workspace-draw.js +17 -18
- package/dist/cli-workspace-draw.js.map +1 -1
- package/dist/cli-workspace-runner.d.ts +30 -0
- package/dist/cli-workspace-runner.d.ts.map +1 -0
- package/dist/cli-workspace-runner.js +105 -0
- package/dist/cli-workspace-runner.js.map +1 -0
- package/dist/mcp-slate-image.js +1 -1
- package/dist/mcp-slate-image.js.map +1 -1
- package/dist/mcp-slate-tool-defs.js +1 -1
- package/dist/mcp-slate-tool-defs.js.map +1 -1
- package/dist/mcp-slate-ui-copy.d.ts +403 -33
- package/dist/mcp-slate-ui-copy.d.ts.map +1 -1
- package/dist/mcp-slate-ui-copy.js +247 -57
- package/dist/mcp-slate-ui-copy.js.map +1 -1
- package/dist/mcp-slate-ui-icu.d.ts +47 -0
- package/dist/mcp-slate-ui-icu.d.ts.map +1 -0
- package/dist/mcp-slate-ui-icu.js +229 -0
- package/dist/mcp-slate-ui-icu.js.map +1 -0
- package/dist/mcp-slate-ui-locale-match.d.ts +12 -0
- package/dist/mcp-slate-ui-locale-match.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locale-match.js +34 -0
- package/dist/mcp-slate-ui-locale-match.js.map +1 -0
- package/dist/mcp-slate-ui-locales/de-DE.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/de-DE.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/de-DE.js +217 -0
- package/dist/mcp-slate-ui-locales/de-DE.js.map +1 -0
- package/dist/mcp-slate-ui-locales/es-419.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/es-419.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/es-419.js +217 -0
- package/dist/mcp-slate-ui-locales/es-419.js.map +1 -0
- package/dist/mcp-slate-ui-locales/es-ES.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/es-ES.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/es-ES.js +217 -0
- package/dist/mcp-slate-ui-locales/es-ES.js.map +1 -0
- package/dist/mcp-slate-ui-locales/fr-FR.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/fr-FR.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/fr-FR.js +217 -0
- package/dist/mcp-slate-ui-locales/fr-FR.js.map +1 -0
- package/dist/mcp-slate-ui-locales/hi-IN.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/hi-IN.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/hi-IN.js +217 -0
- package/dist/mcp-slate-ui-locales/hi-IN.js.map +1 -0
- package/dist/mcp-slate-ui-locales/index.d.ts +11 -0
- package/dist/mcp-slate-ui-locales/index.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/index.js +34 -0
- package/dist/mcp-slate-ui-locales/index.js.map +1 -0
- package/dist/mcp-slate-ui-locales/it-IT.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/it-IT.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/it-IT.js +217 -0
- package/dist/mcp-slate-ui-locales/it-IT.js.map +1 -0
- package/dist/mcp-slate-ui-locales/ja-JP.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/ja-JP.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/ja-JP.js +217 -0
- package/dist/mcp-slate-ui-locales/ja-JP.js.map +1 -0
- package/dist/mcp-slate-ui-locales/ko-KR.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/ko-KR.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/ko-KR.js +217 -0
- package/dist/mcp-slate-ui-locales/ko-KR.js.map +1 -0
- package/dist/mcp-slate-ui-locales/pt-BR.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/pt-BR.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/pt-BR.js +217 -0
- package/dist/mcp-slate-ui-locales/pt-BR.js.map +1 -0
- package/dist/mcp-slate-ui-locales/zh-CN.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/zh-CN.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/zh-CN.js +217 -0
- package/dist/mcp-slate-ui-locales/zh-CN.js.map +1 -0
- package/dist/mcp-slate-ui-locales/zh-TW.d.ts +9 -0
- package/dist/mcp-slate-ui-locales/zh-TW.d.ts.map +1 -0
- package/dist/mcp-slate-ui-locales/zh-TW.js +217 -0
- package/dist/mcp-slate-ui-locales/zh-TW.js.map +1 -0
- package/dist/mcp-slate-ui-script-bridge.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-bridge.js +8 -9
- package/dist/mcp-slate-ui-script-bridge.js.map +1 -1
- package/dist/mcp-slate-ui-script-dialog.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-dialog.js +60 -30
- package/dist/mcp-slate-ui-script-dialog.js.map +1 -1
- package/dist/mcp-slate-ui-script-inline.d.ts +9 -6
- package/dist/mcp-slate-ui-script-inline.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-inline.js +91 -46
- package/dist/mcp-slate-ui-script-inline.js.map +1 -1
- package/dist/mcp-slate-ui-script-inspiration-collage.d.ts +20 -0
- package/dist/mcp-slate-ui-script-inspiration-collage.d.ts.map +1 -0
- package/dist/mcp-slate-ui-script-inspiration-collage.js +68 -0
- package/dist/mcp-slate-ui-script-inspiration-collage.js.map +1 -0
- package/dist/mcp-slate-ui-script-inspiration-grid.d.ts +26 -0
- package/dist/mcp-slate-ui-script-inspiration-grid.d.ts.map +1 -0
- package/dist/mcp-slate-ui-script-inspiration-grid.js +246 -0
- package/dist/mcp-slate-ui-script-inspiration-grid.js.map +1 -0
- package/dist/mcp-slate-ui-script-inspiration.d.ts +10 -9
- package/dist/mcp-slate-ui-script-inspiration.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-inspiration.js +161 -120
- package/dist/mcp-slate-ui-script-inspiration.js.map +1 -1
- package/dist/mcp-slate-ui-script-l10n.d.ts +29 -0
- package/dist/mcp-slate-ui-script-l10n.d.ts.map +1 -0
- package/dist/mcp-slate-ui-script-l10n.js +135 -0
- package/dist/mcp-slate-ui-script-l10n.js.map +1 -0
- package/dist/mcp-slate-ui-script-more.d.ts +3 -2
- package/dist/mcp-slate-ui-script-more.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-more.js +19 -18
- package/dist/mcp-slate-ui-script-more.js.map +1 -1
- package/dist/mcp-slate-ui-script-pause.d.ts +33 -0
- package/dist/mcp-slate-ui-script-pause.d.ts.map +1 -0
- package/dist/mcp-slate-ui-script-pause.js +217 -0
- package/dist/mcp-slate-ui-script-pause.js.map +1 -0
- package/dist/mcp-slate-ui-script-remote.d.ts +2 -0
- package/dist/mcp-slate-ui-script-remote.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-remote.js +39 -6
- package/dist/mcp-slate-ui-script-remote.js.map +1 -1
- package/dist/mcp-slate-ui-script-scribble.js +1 -1
- package/dist/mcp-slate-ui-script-select.js +6 -6
- package/dist/mcp-slate-ui-script-stage.d.ts +3 -2
- package/dist/mcp-slate-ui-script-stage.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-stage.js +31 -19
- package/dist/mcp-slate-ui-script-stage.js.map +1 -1
- package/dist/mcp-slate-ui-script-state.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script-state.js +77 -57
- package/dist/mcp-slate-ui-script-state.js.map +1 -1
- package/dist/mcp-slate-ui-script.d.ts +2 -2
- package/dist/mcp-slate-ui-script.d.ts.map +1 -1
- package/dist/mcp-slate-ui-script.js +21 -7
- package/dist/mcp-slate-ui-script.js.map +1 -1
- package/dist/mcp-slate-ui-styles-dialog.d.ts +6 -7
- package/dist/mcp-slate-ui-styles-dialog.d.ts.map +1 -1
- package/dist/mcp-slate-ui-styles-dialog.js +13 -29
- package/dist/mcp-slate-ui-styles-dialog.js.map +1 -1
- package/dist/mcp-slate-ui-styles-inspiration.d.ts +18 -0
- package/dist/mcp-slate-ui-styles-inspiration.d.ts.map +1 -0
- package/dist/mcp-slate-ui-styles-inspiration.js +43 -0
- package/dist/mcp-slate-ui-styles-inspiration.js.map +1 -0
- package/dist/mcp-slate-ui-styles-pause.d.ts +10 -0
- package/dist/mcp-slate-ui-styles-pause.d.ts.map +1 -0
- package/dist/mcp-slate-ui-styles-pause.js +25 -0
- package/dist/mcp-slate-ui-styles-pause.js.map +1 -0
- package/dist/mcp-slate-ui-styles-stage.d.ts +10 -6
- package/dist/mcp-slate-ui-styles-stage.d.ts.map +1 -1
- package/dist/mcp-slate-ui-styles-stage.js +24 -16
- package/dist/mcp-slate-ui-styles-stage.js.map +1 -1
- package/dist/mcp-slate-ui-styles.d.ts +12 -5
- package/dist/mcp-slate-ui-styles.d.ts.map +1 -1
- package/dist/mcp-slate-ui-styles.js +38 -21
- package/dist/mcp-slate-ui-styles.js.map +1 -1
- package/dist/mcp-slate-ui.d.ts +11 -3
- package/dist/mcp-slate-ui.d.ts.map +1 -1
- package/dist/mcp-slate-ui.js +24 -16
- package/dist/mcp-slate-ui.js.map +1 -1
- package/dist/mosaic-bands.d.ts +44 -0
- package/dist/mosaic-bands.d.ts.map +1 -0
- package/dist/mosaic-bands.js +153 -0
- package/dist/mosaic-bands.js.map +1 -0
- package/dist/review-discovery.d.ts.map +1 -1
- package/dist/review-discovery.js +36 -54
- package/dist/review-discovery.js.map +1 -1
- package/dist/review-page.d.ts.map +1 -1
- package/dist/review-page.js +2 -1
- package/dist/review-page.js.map +1 -1
- package/dist/review-run-roots.d.ts +40 -0
- package/dist/review-run-roots.d.ts.map +1 -0
- package/dist/review-run-roots.js +94 -0
- package/dist/review-run-roots.js.map +1 -0
- package/dist/review-server.d.ts +2 -0
- package/dist/review-server.d.ts.map +1 -1
- package/dist/review-server.js +2 -0
- package/dist/review-server.js.map +1 -1
- package/dist/review-types.d.ts +10 -0
- package/dist/review-types.d.ts.map +1 -1
- package/dist/slate-actions.d.ts +3 -3
- package/dist/slate-actions.d.ts.map +1 -1
- package/dist/slate-actions.js +9 -8
- package/dist/slate-actions.js.map +1 -1
- package/dist/slate-browser-host-script.d.ts +4 -0
- package/dist/slate-browser-host-script.d.ts.map +1 -1
- package/dist/slate-browser-host-script.js +25 -6
- package/dist/slate-browser-host-script.js.map +1 -1
- package/dist/slate-browser-host.d.ts +2 -1
- package/dist/slate-browser-host.d.ts.map +1 -1
- package/dist/slate-browser-host.js +18 -5
- package/dist/slate-browser-host.js.map +1 -1
- package/dist/slate-commit.js +7 -7
- package/dist/slate-commit.js.map +1 -1
- package/dist/slate-corpus.d.ts.map +1 -1
- package/dist/slate-corpus.js +3 -2
- package/dist/slate-corpus.js.map +1 -1
- package/dist/slate-edit-runner.d.ts.map +1 -1
- package/dist/slate-edit-runner.js +5 -4
- package/dist/slate-edit-runner.js.map +1 -1
- package/dist/slate-editability.d.ts +2 -0
- package/dist/slate-editability.d.ts.map +1 -1
- package/dist/slate-editability.js +1 -1
- package/dist/slate-editability.js.map +1 -1
- package/dist/slate-errors.d.ts +7 -1
- package/dist/slate-errors.d.ts.map +1 -1
- package/dist/slate-errors.js +3 -2
- package/dist/slate-errors.js.map +1 -1
- package/dist/slate-handoff-options.d.ts +2 -1
- package/dist/slate-handoff-options.d.ts.map +1 -1
- package/dist/slate-handoff-options.js +2 -2
- package/dist/slate-handoff-options.js.map +1 -1
- package/dist/slate-handoff.js +1 -1
- package/dist/slate-handoff.js.map +1 -1
- package/dist/slate-image-executor.d.ts +18 -1
- package/dist/slate-image-executor.d.ts.map +1 -1
- package/dist/slate-image-executor.js +1 -0
- package/dist/slate-image-executor.js.map +1 -1
- package/dist/slate-inspiration.js +2 -2
- package/dist/slate-inspiration.js.map +1 -1
- package/dist/slate-local-image.d.ts.map +1 -1
- package/dist/slate-local-image.js +4 -1
- package/dist/slate-local-image.js.map +1 -1
- package/dist/slate-lock.js +1 -1
- package/dist/slate-lock.js.map +1 -1
- package/dist/slate-resume.js +6 -6
- package/dist/slate-resume.js.map +1 -1
- package/dist/slate-round-params.d.ts +3 -0
- package/dist/slate-round-params.d.ts.map +1 -1
- package/dist/slate-round-params.js +5 -0
- package/dist/slate-round-params.js.map +1 -1
- package/dist/slate-round-queries.d.ts +18 -0
- package/dist/slate-round-queries.d.ts.map +1 -0
- package/dist/slate-round-queries.js +29 -0
- package/dist/slate-round-queries.js.map +1 -0
- package/dist/slate-round-runner.d.ts.map +1 -1
- package/dist/slate-round-runner.js +12 -9
- package/dist/slate-round-runner.js.map +1 -1
- package/dist/slate-round-search.js +5 -5
- package/dist/slate-round-search.js.map +1 -1
- package/dist/slate-run-action.d.ts.map +1 -1
- package/dist/slate-run-action.js +2 -1
- package/dist/slate-run-action.js.map +1 -1
- package/dist/slate-runner-context.d.ts +3 -1
- package/dist/slate-runner-context.d.ts.map +1 -1
- package/dist/slate-runner-context.js +4 -3
- package/dist/slate-runner-context.js.map +1 -1
- package/dist/slate-runner-failure.d.ts +7 -1
- package/dist/slate-runner-failure.d.ts.map +1 -1
- package/dist/slate-runner-failure.js +6 -5
- package/dist/slate-runner-failure.js.map +1 -1
- package/dist/slate-runtime.d.ts.map +1 -1
- package/dist/slate-runtime.js +1 -0
- package/dist/slate-runtime.js.map +1 -1
- package/dist/slate-versions.js +3 -3
- package/dist/slate-versions.js.map +1 -1
- package/dist/slate-view-contract.d.ts +62 -1
- package/dist/slate-view-contract.d.ts.map +1 -1
- package/dist/slate-view-contract.js +30 -0
- package/dist/slate-view-contract.js.map +1 -1
- package/dist/slate-view.d.ts.map +1 -1
- package/dist/slate-view.js +19 -3
- package/dist/slate-view.js.map +1 -1
- package/dist/slate-workspace-parse.d.ts.map +1 -1
- package/dist/slate-workspace-parse.js +22 -0
- package/dist/slate-workspace-parse.js.map +1 -1
- package/dist/slate-workspace-types.d.ts +2 -0
- package/dist/slate-workspace-types.d.ts.map +1 -1
- package/dist/workspace-draw-client.d.ts +42 -1
- package/dist/workspace-draw-client.d.ts.map +1 -1
- package/dist/workspace-draw-client.js +56 -7
- package/dist/workspace-draw-client.js.map +1 -1
- package/dist/workspace-draw-codex.d.ts +9 -4
- package/dist/workspace-draw-codex.d.ts.map +1 -1
- package/dist/workspace-draw-codex.js +17 -3
- package/dist/workspace-draw-codex.js.map +1 -1
- package/dist/workspace-draw-messages.d.ts +24 -0
- package/dist/workspace-draw-messages.d.ts.map +1 -1
- package/dist/workspace-draw-messages.js +25 -0
- package/dist/workspace-draw-messages.js.map +1 -1
- package/dist/workspace-draw-run.d.ts +19 -1
- package/dist/workspace-draw-run.d.ts.map +1 -1
- package/dist/workspace-draw-run.js +76 -3
- package/dist/workspace-draw-run.js.map +1 -1
- package/dist/workspace-draw-store.d.ts +2 -0
- package/dist/workspace-draw-store.d.ts.map +1 -1
- package/dist/workspace-draw-store.js +3 -1
- package/dist/workspace-draw-store.js.map +1 -1
- package/dist/workspace-draw-types.d.ts +1 -0
- package/dist/workspace-draw-types.d.ts.map +1 -1
- package/dist/workspace-draw-types.js +1 -0
- package/dist/workspace-draw-types.js.map +1 -1
- package/dist/workspace-origin.d.ts +15 -0
- package/dist/workspace-origin.d.ts.map +1 -0
- package/dist/workspace-origin.js +36 -0
- package/dist/workspace-origin.js.map +1 -0
- package/dist/workspace-runner-children.d.ts +28 -0
- package/dist/workspace-runner-children.d.ts.map +1 -0
- package/dist/workspace-runner-children.js +112 -0
- package/dist/workspace-runner-children.js.map +1 -0
- package/dist/workspace-runner-client.d.ts +41 -0
- package/dist/workspace-runner-client.d.ts.map +1 -0
- package/dist/workspace-runner-client.js +63 -0
- package/dist/workspace-runner-client.js.map +1 -0
- package/dist/workspace-runner-main.d.ts +56 -0
- package/dist/workspace-runner-main.d.ts.map +1 -0
- package/dist/workspace-runner-main.js +350 -0
- package/dist/workspace-runner-main.js.map +1 -0
- package/dist/workspace-runner-policy.d.ts +40 -0
- package/dist/workspace-runner-policy.d.ts.map +1 -0
- package/dist/workspace-runner-policy.js +47 -0
- package/dist/workspace-runner-policy.js.map +1 -0
- package/dist/workspace-runner-spawn.d.ts +42 -0
- package/dist/workspace-runner-spawn.d.ts.map +1 -0
- package/dist/workspace-runner-spawn.js +60 -0
- package/dist/workspace-runner-spawn.js.map +1 -0
- package/dist/workspace-runner-stop.d.ts +25 -0
- package/dist/workspace-runner-stop.d.ts.map +1 -0
- package/dist/workspace-runner-stop.js +44 -0
- package/dist/workspace-runner-stop.js.map +1 -0
- package/dist/workspace-runner-store.d.ts +26 -0
- package/dist/workspace-runner-store.d.ts.map +1 -0
- package/dist/workspace-runner-store.js +114 -0
- package/dist/workspace-runner-store.js.map +1 -0
- package/open-design.json +2 -2
- package/package.json +1 -1
- package/runtime/codex-conversion/src/images/native-invoke.mjs +24 -5
- package/runtime/codex-conversion/src/images/native.mjs +3 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type RunnerLaunchRefusal } from './workspace-runner-policy.js';
|
|
2
|
+
/**
|
|
3
|
+
* Leave the workspace runner behind (docs/slate-remote.md, "The workspace runner"). `12ui workspace draw` calls this
|
|
4
|
+
* once the token works (it read a plan, or was told the round waits on the person) and Codex can draw here. Nothing at
|
|
5
|
+
* all happens when the person opted out (`TWELVE_UI_NO_RUNNER=1`) or the origin is not design.12ui.com (or this
|
|
6
|
+
* machine); otherwise it stores the token it was given (the newest one reaches a running runner at its next
|
|
7
|
+
* heartbeat), and starts a runner only when no live one holds the workspace's lock on this Mac.
|
|
8
|
+
*
|
|
9
|
+
* The child gets its own session (`detached`, so `setsid`), no stdio, and is unref'd: Codex ends the shell command's
|
|
10
|
+
* process group when the command returns, and a child in its own session is not in that group. Its working
|
|
11
|
+
* directory is its own state folder. Its environment is an allowlist: what `codex exec` needs to find itself, its
|
|
12
|
+
* sign-in and its network, and where the 12ui data root is; no 12ui, OpenAI or other credential, and not the token.
|
|
13
|
+
*/
|
|
14
|
+
export declare const WORKSPACE_RUNNER_ENV_FLAG = "TWELVE_UI_WORKSPACE_RUNNER";
|
|
15
|
+
export declare const workspaceRunnerEnvironment: (parent: NodeJS.ProcessEnv) => NodeJS.ProcessEnv;
|
|
16
|
+
export type RunnerLaunch = {
|
|
17
|
+
state: 'started' | 'running';
|
|
18
|
+
pid: number | null;
|
|
19
|
+
dir: string;
|
|
20
|
+
}
|
|
21
|
+
/** No runner, and nothing written: the person opted out, or the origin may not have one. */
|
|
22
|
+
| {
|
|
23
|
+
state: 'off';
|
|
24
|
+
reason: RunnerLaunchRefusal;
|
|
25
|
+
};
|
|
26
|
+
export type RunnerLauncher = (args: {
|
|
27
|
+
origin: string;
|
|
28
|
+
token: string;
|
|
29
|
+
workspace: string;
|
|
30
|
+
dataRoot: string;
|
|
31
|
+
env: NodeJS.ProcessEnv;
|
|
32
|
+
}) => Promise<RunnerLaunch>;
|
|
33
|
+
export type RunnerSpawn = (args: {
|
|
34
|
+
origin: string;
|
|
35
|
+
workspace: string;
|
|
36
|
+
dir: string;
|
|
37
|
+
env: NodeJS.ProcessEnv;
|
|
38
|
+
}) => Promise<number | null>;
|
|
39
|
+
/** Start `12ui workspace runner` as a detached process of this same CLI build. Resolves with its PID once it spawned. */
|
|
40
|
+
export declare const spawnWorkspaceRunner: RunnerSpawn;
|
|
41
|
+
export declare const createRunnerLauncher: (spawnRunner?: RunnerSpawn) => RunnerLauncher;
|
|
42
|
+
//# sourceMappingURL=workspace-runner-spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-spawn.d.ts","sourceRoot":"","sources":["../src/workspace-runner-spawn.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAG7F;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AAStE,eAAO,MAAM,0BAA0B,GAAI,QAAQ,MAAM,CAAC,UAAU,KAAG,MAAM,CAAC,UAI7E,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AACnE,4FAA4F;GAC1F;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEvI,yHAAyH;AACzH,eAAO,MAAM,oBAAoB,EAAE,WAclC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,cAAa,WAAkC,KAAG,cAUtF,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { runnerLaunchRefusal } from './workspace-runner-policy.js';
|
|
4
|
+
import { ensureRunnerDir, liveRunnerHolder, runnerDirOf, writeRunnerToken } from './workspace-runner-store.js';
|
|
5
|
+
/**
|
|
6
|
+
* Leave the workspace runner behind (docs/slate-remote.md, "The workspace runner"). `12ui workspace draw` calls this
|
|
7
|
+
* once the token works (it read a plan, or was told the round waits on the person) and Codex can draw here. Nothing at
|
|
8
|
+
* all happens when the person opted out (`TWELVE_UI_NO_RUNNER=1`) or the origin is not design.12ui.com (or this
|
|
9
|
+
* machine); otherwise it stores the token it was given (the newest one reaches a running runner at its next
|
|
10
|
+
* heartbeat), and starts a runner only when no live one holds the workspace's lock on this Mac.
|
|
11
|
+
*
|
|
12
|
+
* The child gets its own session (`detached`, so `setsid`), no stdio, and is unref'd: Codex ends the shell command's
|
|
13
|
+
* process group when the command returns, and a child in its own session is not in that group. Its working
|
|
14
|
+
* directory is its own state folder. Its environment is an allowlist: what `codex exec` needs to find itself, its
|
|
15
|
+
* sign-in and its network, and where the 12ui data root is; no 12ui, OpenAI or other credential, and not the token.
|
|
16
|
+
*/
|
|
17
|
+
export const WORKSPACE_RUNNER_ENV_FLAG = 'TWELVE_UI_WORKSPACE_RUNNER';
|
|
18
|
+
const INHERITED = [
|
|
19
|
+
'PATH', 'HOME', 'USER', 'LOGNAME', 'TMPDIR', 'TEMP', 'TMP', 'LANG', 'LC_ALL', 'SYSTEMROOT', 'APPDATA', 'LOCALAPPDATA',
|
|
20
|
+
'XDG_DATA_HOME', 'CODEX_HOME', 'CODEX_CLI_PATH', 'TWELVE_UI_CODEX_BIN', 'TWELVE_UI_INSTALL_SOURCE', 'TWELVE_UI_BUNDLE_ROOT',
|
|
21
|
+
'HTTPS_PROXY', 'HTTP_PROXY', 'NO_PROXY', 'ALL_PROXY', 'https_proxy', 'http_proxy', 'no_proxy', 'all_proxy',
|
|
22
|
+
'SSL_CERT_FILE', 'NODE_EXTRA_CA_CERTS',
|
|
23
|
+
];
|
|
24
|
+
export const workspaceRunnerEnvironment = (parent) => {
|
|
25
|
+
const environment = { [WORKSPACE_RUNNER_ENV_FLAG]: '1' };
|
|
26
|
+
for (const key of INHERITED)
|
|
27
|
+
if (parent[key])
|
|
28
|
+
environment[key] = parent[key];
|
|
29
|
+
return environment;
|
|
30
|
+
};
|
|
31
|
+
/** Start `12ui workspace runner` as a detached process of this same CLI build. Resolves with its PID once it spawned. */
|
|
32
|
+
export const spawnWorkspaceRunner = async (args) => {
|
|
33
|
+
const cliPath = fileURLToPath(new URL('./cli.js', import.meta.url));
|
|
34
|
+
const child = spawn(process.execPath, [cliPath, 'workspace', 'runner', '--origin', args.origin, '--workspace', args.workspace], {
|
|
35
|
+
detached: true,
|
|
36
|
+
stdio: 'ignore',
|
|
37
|
+
cwd: args.dir,
|
|
38
|
+
env: workspaceRunnerEnvironment(args.env),
|
|
39
|
+
});
|
|
40
|
+
await new Promise((resolve, reject) => {
|
|
41
|
+
child.once('spawn', () => { resolve(); });
|
|
42
|
+
child.once('error', reject);
|
|
43
|
+
});
|
|
44
|
+
child.unref();
|
|
45
|
+
return child.pid ?? null;
|
|
46
|
+
};
|
|
47
|
+
export const createRunnerLauncher = (spawnRunner = spawnWorkspaceRunner) => async (args) => {
|
|
48
|
+
const refusal = runnerLaunchRefusal(args.origin, args.env);
|
|
49
|
+
if (refusal)
|
|
50
|
+
return { state: 'off', reason: refusal };
|
|
51
|
+
const dir = runnerDirOf(args.dataRoot, args.origin, args.workspace);
|
|
52
|
+
await ensureRunnerDir(dir);
|
|
53
|
+
await writeRunnerToken(dir, args.token);
|
|
54
|
+
const live = await liveRunnerHolder(dir);
|
|
55
|
+
if (live)
|
|
56
|
+
return { state: 'running', pid: live.pid, dir };
|
|
57
|
+
// Two draws racing here may both spawn: the runner that loses the lock exits at once.
|
|
58
|
+
return { state: 'started', pid: await spawnRunner({ origin: args.origin, workspace: args.workspace, dir, env: args.env }), dir };
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=workspace-runner-spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-spawn.js","sourceRoot":"","sources":["../src/workspace-runner-spawn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `12ui workspace runner stop`: the person's way to end the workspace runners on this Mac. Each live runner gets
|
|
3
|
+
* SIGTERM (it claims nothing more, finishes the drawing in hand, says goodbye, deletes its token and exits); a folder
|
|
4
|
+
* whose runner is gone has its leftover token deleted; and every Codex call a killed runner left running is stopped.
|
|
5
|
+
*/
|
|
6
|
+
export type StopScope = {
|
|
7
|
+
all: true;
|
|
8
|
+
} | {
|
|
9
|
+
all: false;
|
|
10
|
+
origin: string;
|
|
11
|
+
workspace: string | null;
|
|
12
|
+
};
|
|
13
|
+
export type StopReport = {
|
|
14
|
+
stopped: Array<{
|
|
15
|
+
workspace: string;
|
|
16
|
+
pid: number;
|
|
17
|
+
}>;
|
|
18
|
+
codexStopped: number[];
|
|
19
|
+
};
|
|
20
|
+
export declare const stopWorkspaceRunners: (args: {
|
|
21
|
+
dataRoot: string;
|
|
22
|
+
scope: StopScope;
|
|
23
|
+
}) => Promise<StopReport>;
|
|
24
|
+
export declare const stopMessage: (report: StopReport) => string;
|
|
25
|
+
//# sourceMappingURL=workspace-runner-stop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-stop.d.ts","sourceRoot":"","sources":["../src/workspace-runner-stop.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AAEH,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEjG,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AASF,eAAO,MAAM,oBAAoB,GAAU,MAAM;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,KAAG,OAAO,CAAC,UAAU,CAmB3G,CAAC;AAIF,eAAO,MAAM,WAAW,GAAI,QAAQ,UAAU,KAAG,MAKhD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { reapCodexChildren } from './workspace-runner-children.js';
|
|
3
|
+
import { listRunnerDirs, liveRunnerHolder, removeRunnerToken, runnerDirOf } from './workspace-runner-store.js';
|
|
4
|
+
const folders = async (dataRoot, scope) => {
|
|
5
|
+
if (scope.all)
|
|
6
|
+
return await listRunnerDirs(dataRoot, null);
|
|
7
|
+
if (scope.workspace === null)
|
|
8
|
+
return await listRunnerDirs(dataRoot, scope.origin);
|
|
9
|
+
const dir = runnerDirOf(dataRoot, scope.origin, scope.workspace);
|
|
10
|
+
return existsSync(dir) ? [{ dir, workspace: scope.workspace }] : [];
|
|
11
|
+
};
|
|
12
|
+
export const stopWorkspaceRunners = async (args) => {
|
|
13
|
+
const report = { stopped: [], codexStopped: [] };
|
|
14
|
+
for (const { dir, workspace } of await folders(args.dataRoot, args.scope)) {
|
|
15
|
+
const holder = await liveRunnerHolder(dir);
|
|
16
|
+
if (holder) {
|
|
17
|
+
try {
|
|
18
|
+
process.kill(holder.pid, 'SIGTERM');
|
|
19
|
+
report.stopped.push({ workspace, pid: holder.pid });
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code !== 'ESRCH')
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
await removeRunnerToken(dir);
|
|
28
|
+
}
|
|
29
|
+
// A live runner finishes its own calls; a call whose runner is gone was left behind by a killed one.
|
|
30
|
+
const reaped = await reapCodexChildren(dir);
|
|
31
|
+
report.codexStopped.push(...reaped.filter((entry) => entry.outcome === 'stopped').map((entry) => entry.pgid));
|
|
32
|
+
}
|
|
33
|
+
return report;
|
|
34
|
+
};
|
|
35
|
+
const plural = (count, word) => `${count} ${word}${count === 1 ? '' : 's'}`;
|
|
36
|
+
export const stopMessage = (report) => {
|
|
37
|
+
const parts = [];
|
|
38
|
+
if (report.stopped.length > 0)
|
|
39
|
+
parts.push(`Stopped ${plural(report.stopped.length, 'workspace runner')}; each finishes the drawing in hand, then exits.`);
|
|
40
|
+
if (report.codexStopped.length > 0)
|
|
41
|
+
parts.push(`Stopped ${plural(report.codexStopped.length, 'Codex call')} a killed runner left behind.`);
|
|
42
|
+
return parts.length > 0 ? parts.join(' ') : 'No workspace runner is running on this Mac.';
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=workspace-runner-stop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-stop.js","sourceRoot":"","sources":["../src/workspace-runner-stop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type LockHolder } from './slate-lock.js';
|
|
2
|
+
/** A workspace's public key as the plan names it (`wk_` and 8 hex on design.12ui.com): path-safe by construction. */
|
|
3
|
+
export declare const WORKSPACE_KEY_PATTERN: RegExp;
|
|
4
|
+
/** The origin is part of the key, so a local proof server and design.12ui.com never share a runner. */
|
|
5
|
+
export declare const runnerDirOf: (dataRoot: string, origin: string, workspace: string) => string;
|
|
6
|
+
/** Every runner folder on this Mac, or only those of one origin: `{dir, workspace}`. */
|
|
7
|
+
export declare const listRunnerDirs: (dataRoot: string, origin: string | null) => Promise<Array<{
|
|
8
|
+
dir: string;
|
|
9
|
+
workspace: string;
|
|
10
|
+
}>>;
|
|
11
|
+
export declare const runnerLockFile: (dir: string) => string;
|
|
12
|
+
export declare const ensureRunnerDir: (dir: string) => Promise<void>;
|
|
13
|
+
/** Replace the token atomically (0600): a reader sees the old token or the new one, never half of either. */
|
|
14
|
+
export declare const writeRunnerToken: (dir: string, token: string) => Promise<void>;
|
|
15
|
+
/** The stored token, or null when it is missing or not a token. */
|
|
16
|
+
export declare const readRunnerToken: (dir: string) => Promise<string | null>;
|
|
17
|
+
/** When the stored token was written (its mtime), or null when there is none. */
|
|
18
|
+
export declare const runnerTokenWrittenAtMs: (dir: string) => Promise<number | null>;
|
|
19
|
+
/** The runner deletes the token when it exits, so no live credential is left on disk without a runner. */
|
|
20
|
+
export declare const removeRunnerToken: (dir: string) => Promise<void>;
|
|
21
|
+
/** The live runner's holder, or null when none holds the lock (absent, its PID gone, or its heartbeat lapsed). */
|
|
22
|
+
export declare const liveRunnerHolder: (dir: string) => Promise<LockHolder | null>;
|
|
23
|
+
/** One JSON line in `runner.log`. Diagnostics only: a failed write never stops the runner. */
|
|
24
|
+
export declare const createRunnerLog: (dir: string, clock?: () => Date) => (entry: Record<string, unknown>) => Promise<void>;
|
|
25
|
+
export type RunnerLog = ReturnType<typeof createRunnerLog>;
|
|
26
|
+
//# sourceMappingURL=workspace-runner-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-store.d.ts","sourceRoot":"","sources":["../src/workspace-runner-store.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgBzF,qHAAqH;AACrH,eAAO,MAAM,qBAAqB,QAA4B,CAAC;AAQ/D,uGAAuG;AACvG,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,EAAE,WAAW,MAAM,KAAG,MAGjF,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,cAAc,GAAU,UAAU,MAAM,EAAE,QAAQ,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAa/H,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAgC,CAAC;AAI9E,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAG/D,CAAC;AAEF,6GAA6G;AAC7G,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAU/E,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAQxE,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAO/E,CAAC;AAEF,0GAA0G;AAC1G,eAAO,MAAM,iBAAiB,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAEjE,CAAC;AAEF,kHAAkH;AAClH,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAG7E,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,EAAE,QAAO,MAAM,IAAuB,MAAY,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,IAAI,CAUzI,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { appendFile, chmod, mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { holderState, staleWhenHeartbeatLapsed } from './slate-lock.js';
|
|
5
|
+
/**
|
|
6
|
+
* Where the workspace runner keeps its state on this Mac: `<data root>/workspace-runner/<origin key>-<workspace>/`,
|
|
7
|
+
* a folder only this user can read (0700), holding
|
|
8
|
+
* - `lock`: the single-instance lock (alive while the runner's PID is and its heartbeat, the mtime, is fresh);
|
|
9
|
+
* - `token`: the workspace's newest upload token (0600), written by every `12ui workspace draw` for that workspace
|
|
10
|
+
* and re-read by the runner before each heartbeat, so a fresher token reaches a running runner without a restart;
|
|
11
|
+
* its mtime is when it was written, and the runner deletes it when it exits;
|
|
12
|
+
* - `codex/<pgid>.json`: each Codex image call the runner has running, by its process group, so a call a killed
|
|
13
|
+
* runner left behind can be found and stopped (workspace-runner-children.ts);
|
|
14
|
+
* - `runner.log`: what it did, JSON lines, rotated at 1 MiB (its stdio goes nowhere).
|
|
15
|
+
* The token is never put on a command line or in the environment of the detached process.
|
|
16
|
+
*/
|
|
17
|
+
const TOKEN_PATTERN = /^wp_[0-9A-Za-z]{22}$/u;
|
|
18
|
+
/** A workspace's public key as the plan names it (`wk_` and 8 hex on design.12ui.com): path-safe by construction. */
|
|
19
|
+
export const WORKSPACE_KEY_PATTERN = /^wk_[0-9A-Za-z]{1,32}$/u;
|
|
20
|
+
const LOG_LIMIT_BYTES = 1024 * 1024;
|
|
21
|
+
const sha256 = (value) => createHash('sha256').update(value).digest('hex');
|
|
22
|
+
const runnersRoot = (dataRoot) => path.join(dataRoot, 'workspace-runner');
|
|
23
|
+
const originKey = (origin) => sha256(origin).slice(0, 12);
|
|
24
|
+
/** The origin is part of the key, so a local proof server and design.12ui.com never share a runner. */
|
|
25
|
+
export const runnerDirOf = (dataRoot, origin, workspace) => {
|
|
26
|
+
if (!WORKSPACE_KEY_PATTERN.test(workspace))
|
|
27
|
+
throw new Error(`${JSON.stringify(workspace)} is not a workspace key.`);
|
|
28
|
+
return path.join(runnersRoot(dataRoot), `${originKey(origin)}-${workspace}`);
|
|
29
|
+
};
|
|
30
|
+
/** Every runner folder on this Mac, or only those of one origin: `{dir, workspace}`. */
|
|
31
|
+
export const listRunnerDirs = async (dataRoot, origin) => {
|
|
32
|
+
let names;
|
|
33
|
+
try {
|
|
34
|
+
names = await readdir(runnersRoot(dataRoot));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code === 'ENOENT')
|
|
38
|
+
return [];
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
return names.flatMap((name) => {
|
|
42
|
+
const match = /^([0-9a-f]{12})-(wk_[0-9A-Za-z]{1,32})$/u.exec(name);
|
|
43
|
+
if (!match || (origin !== null && match[1] !== originKey(origin)))
|
|
44
|
+
return [];
|
|
45
|
+
return [{ dir: path.join(runnersRoot(dataRoot), name), workspace: match[2] }];
|
|
46
|
+
}).sort((a, b) => a.dir.localeCompare(b.dir));
|
|
47
|
+
};
|
|
48
|
+
export const runnerLockFile = (dir) => path.join(dir, 'lock');
|
|
49
|
+
const tokenFile = (dir) => path.join(dir, 'token');
|
|
50
|
+
const logFile = (dir) => path.join(dir, 'runner.log');
|
|
51
|
+
export const ensureRunnerDir = async (dir) => {
|
|
52
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
53
|
+
await chmod(dir, 0o700);
|
|
54
|
+
};
|
|
55
|
+
/** Replace the token atomically (0600): a reader sees the old token or the new one, never half of either. */
|
|
56
|
+
export const writeRunnerToken = async (dir, token) => {
|
|
57
|
+
if (!TOKEN_PATTERN.test(token))
|
|
58
|
+
throw new Error('Refusing to store something that is not an upload token.');
|
|
59
|
+
const file = tokenFile(dir);
|
|
60
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
61
|
+
try {
|
|
62
|
+
await writeFile(temporary, `${token}\n`, { flag: 'wx', mode: 0o600 });
|
|
63
|
+
await rename(temporary, file);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
await rm(temporary, { force: true });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** The stored token, or null when it is missing or not a token. */
|
|
70
|
+
export const readRunnerToken = async (dir) => {
|
|
71
|
+
try {
|
|
72
|
+
const value = (await readFile(tokenFile(dir), 'utf8')).trim();
|
|
73
|
+
return TOKEN_PATTERN.test(value) ? value : null;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code === 'ENOENT')
|
|
77
|
+
return null;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** When the stored token was written (its mtime), or null when there is none. */
|
|
82
|
+
export const runnerTokenWrittenAtMs = async (dir) => {
|
|
83
|
+
try {
|
|
84
|
+
return (await stat(tokenFile(dir))).mtimeMs;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error.code === 'ENOENT')
|
|
88
|
+
return null;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/** The runner deletes the token when it exits, so no live credential is left on disk without a runner. */
|
|
93
|
+
export const removeRunnerToken = async (dir) => {
|
|
94
|
+
await rm(tokenFile(dir), { force: true });
|
|
95
|
+
};
|
|
96
|
+
/** The live runner's holder, or null when none holds the lock (absent, its PID gone, or its heartbeat lapsed). */
|
|
97
|
+
export const liveRunnerHolder = async (dir) => {
|
|
98
|
+
const state = await holderState(runnerLockFile(dir), staleWhenHeartbeatLapsed);
|
|
99
|
+
return state.state === 'live' ? state.holder ?? null : null;
|
|
100
|
+
};
|
|
101
|
+
/** One JSON line in `runner.log`. Diagnostics only: a failed write never stops the runner. */
|
|
102
|
+
export const createRunnerLog = (dir, clock = () => new Date()) => async (entry) => {
|
|
103
|
+
const file = logFile(dir);
|
|
104
|
+
try {
|
|
105
|
+
const size = await stat(file).then((info) => info.size, () => 0);
|
|
106
|
+
if (size > LOG_LIMIT_BYTES) {
|
|
107
|
+
await rm(`${file}.1`, { force: true });
|
|
108
|
+
await rename(file, `${file}.1`);
|
|
109
|
+
}
|
|
110
|
+
await appendFile(file, `${JSON.stringify({ at: clock().toISOString(), pid: process.pid, ...entry })}\n`, { mode: 0o600 });
|
|
111
|
+
}
|
|
112
|
+
catch { /* the log is diagnostics only */ }
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=workspace-runner-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-runner-store.js","sourceRoot":"","sources":["../src/workspace-runner-store.ts"],"names":[],"mappings":"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"}
|
package/open-design.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"specVersion": "1.0.0",
|
|
4
4
|
"name": "12ui-design",
|
|
5
5
|
"title": "12UI Design",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.77",
|
|
7
7
|
"description": "Explore corpus-grounded interface directions, choose one, and convert it into production-ready responsive or native artifacts with 12UI.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Just Every",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"command": "npx",
|
|
50
50
|
"args": [
|
|
51
51
|
"-y",
|
|
52
|
-
"@12ui/design@0.2.
|
|
52
|
+
"@12ui/design@0.2.77",
|
|
53
53
|
"mcp"
|
|
54
54
|
]
|
|
55
55
|
}
|
package/package.json
CHANGED
|
@@ -8,13 +8,26 @@ import {filesUnder} from './native-files.mjs';
|
|
|
8
8
|
import {collectNative,packNativeReceipt} from './native-collect.mjs';
|
|
9
9
|
|
|
10
10
|
// Based on the successful C-N03 study invocation; separate from the no-tool planner.
|
|
11
|
+
// The call is unattended (`-a never`) and its prompt carries text from outside (a person's typed edit, a plan built
|
|
12
|
+
// from a source image), so its sandbox is read-only: the model may read the attached images and call the image tool,
|
|
13
|
+
// whose output Codex itself records in the rollout and its own home, but any command it is talked into cannot write.
|
|
14
|
+
// Proven with real Codex 0.159.3 (docs/slate-remote.md, "Image calls are read-only").
|
|
15
|
+
export const NATIVE_SANDBOX = 'read-only';
|
|
11
16
|
export function nativeArgv(spec, finalPath) {
|
|
12
17
|
return ['-a', 'never', 'exec', '--ignore-user-config', '--ignore-rules', '--strict-config', '-C', spec.workDir,
|
|
13
|
-
'--skip-git-repo-check', '-s',
|
|
18
|
+
'--skip-git-repo-check', '-s', NATIVE_SANDBOX, '-m', 'gpt-6-astra', '-c', 'model_reasoning_effort="medium"',
|
|
14
19
|
'-c', 'model_provider="openai"', '-c', 'forced_login_method="chatgpt"', '-c', 'project_doc_max_bytes=0',
|
|
15
20
|
'--enable', 'skip_host_skill_discovery', '--json', '--color', 'never', '--output-last-message', finalPath,
|
|
16
21
|
...(spec.referencePaths ?? [spec.referencePath]).flatMap(file => ['--image', file]), '-'];
|
|
17
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* One native image call: `codex exec` in its own process group, its prompt on stdin, its events and stderr kept in the
|
|
25
|
+
* call directory. Optional in `spec`:
|
|
26
|
+
* - `interruptOnSignal` (default true): a SIGINT or SIGTERM of this process stops the call. A caller that finishes its
|
|
27
|
+
* calls on a signal itself (the Design workspace runner) passes false, so the call runs to its own end or timeout.
|
|
28
|
+
* - `onSpawn({pgid, callDir})` / `onExit({pgid})`: told the child's process group (it leads its own) once it runs and
|
|
29
|
+
* once it is gone, so a caller can find a call it left behind. A failing hook never fails the call.
|
|
30
|
+
*/
|
|
18
31
|
export async function invokeNative(spec, {callDir, persistMetadata}) {
|
|
19
32
|
await fs.mkdir(spec.workDir, {recursive: true});
|
|
20
33
|
const auth = spec.auth;
|
|
@@ -27,15 +40,21 @@ export async function invokeNative(spec, {callDir, persistMetadata}) {
|
|
|
27
40
|
const prompt = spec.prompt;
|
|
28
41
|
let timedOut = false, killTimer;
|
|
29
42
|
const child = spawn(spec.binary, argv, {cwd: spec.workDir, env: subscriptionEnv(auth.home), detached: true, stdio: ['pipe', 'pipe', 'pipe']});
|
|
30
|
-
|
|
43
|
+
// ESRCH: the group is gone. EPERM: macOS answers so for a group whose members are all zombies.
|
|
44
|
+
const stop = signal => { if (child.pid) try { process.kill(-child.pid, signal); } catch (error) { if (error.code !== 'ESRCH' && error.code !== 'EPERM') throw error; } };
|
|
31
45
|
const terminate = () => { stop('SIGTERM'); killTimer = setTimeout(() => stop('SIGKILL'), 5000); };
|
|
46
|
+
const hook = async (name, value) => { try { await spec[name]?.(value); } catch { /* the caller's bookkeeping only */ } };
|
|
47
|
+
const closed = new Promise(resolve => { child.once('error', error => resolve({spawnError: String(error)})); child.once('close', (exitCode, signal) => resolve({exitCode, signal})); });
|
|
32
48
|
child.stdout.pipe(stdout); child.stderr.pipe(stderr); child.stdin.on('error', () => {}); child.stdin.end(prompt);
|
|
33
49
|
const timer = setTimeout(() => { timedOut = true; terminate(); }, spec.timeoutMs);
|
|
50
|
+
const interruptible = spec.interruptOnSignal !== false;
|
|
34
51
|
const interrupted = () => terminate();
|
|
35
|
-
process.once('SIGINT', interrupted); process.once('SIGTERM', interrupted);
|
|
36
|
-
|
|
52
|
+
if (interruptible) { process.once('SIGINT', interrupted); process.once('SIGTERM', interrupted); }
|
|
53
|
+
if (child.pid) await hook('onSpawn', {pgid: child.pid, callDir});
|
|
54
|
+
const result = await closed;
|
|
37
55
|
clearTimeout(timer); clearTimeout(killTimer); stop('SIGTERM');
|
|
38
|
-
process.removeListener('SIGINT', interrupted); process.removeListener('SIGTERM', interrupted);
|
|
56
|
+
if (interruptible) { process.removeListener('SIGINT', interrupted); process.removeListener('SIGTERM', interrupted); }
|
|
57
|
+
if (child.pid) await hook('onExit', {pgid: child.pid});
|
|
39
58
|
await Promise.all([finished(stdout), finished(stderr)]);
|
|
40
59
|
const events=(await fs.readFile(path.join(callDir,'events.jsonl'),'utf8')).split('\n').filter(Boolean).flatMap(line=>{try{return [JSON.parse(line)];}catch{return [{type:'malformed-event'}];}});
|
|
41
60
|
const diagnostics=events.filter(event=>['error','turn.failed','malformed-event'].includes(event.type));
|
|
@@ -18,7 +18,8 @@ export async function decodeNativeResponse(bytes) {
|
|
|
18
18
|
const png=await sharp(original).resize(expected.width,expected.height,{fit:'fill',kernel:'lanczos3'}).png().toBuffer();
|
|
19
19
|
return {png,usage:{codexTokens:receipt.tokenUsage??[],imageUsage:null},revisedPrompt:receipt.generations[0].revisedPrompt??null,native:{originalPath:receipt.outputs[0].path,originalSha256:sha256(original),originalDimensions:actual,normalization:{name:'exact-stretch',kernel:'lanczos3',output:expected,scaleX:expected.width/actual.width,scaleY:expected.height/actual.height},model:receipt.model,effort:receipt.effort,imageModel:null,invocationCount:receipt.invocationCount}};
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/** `processOptions` reaches the call's `codex exec` child: `interruptOnSignal`, `onSpawn`, `onExit` (native-invoke.mjs). */
|
|
22
|
+
export async function executeNativeCall({request,input,callId,store,codexBinary='codex',codexHome,timeoutMs=1800000,processOptions={}}) {
|
|
22
23
|
let auth;
|
|
23
24
|
return store.runCall(callId,request,async({callDir,persistMetadata})=>{
|
|
24
25
|
// One reference is the conversion case; a list attaches every image in order (design workspace edits and variations).
|
|
@@ -29,7 +30,7 @@ export async function executeNativeCall({request,input,callId,store,codexBinary=
|
|
|
29
30
|
const referencePath=referencePaths[0];
|
|
30
31
|
const inspect=referencePaths.length===1?`Use tools.view_image once to inspect ${referencePath}.`:`Use tools.view_image once for each of these files, in this order: ${referencePaths.join(', ')}.`;
|
|
31
32
|
const prompt=`This is an asset-execution task. ${inspect} Then invoke tools.image_gen__imagegen exactly once with referenced_image_paths=[${referencePaths.map(file=>JSON.stringify(file)).join(', ')}] and the following prompt copied verbatim. Do not use shell, web, apps, other image providers, or image-editing code. No retries, extra calls, HTML work, or visual repair. If unavailable or unsuccessful, report failure. Emit the native image result with generatedImage and finish. Do not print base64.\n\nIMAGE PROMPT:\n${request.prompt}\n`;
|
|
32
|
-
return invokeNative({auth,binary:codexBinary,workDir:path.join(callDir,'work'),referencePath,referencePaths,prompt,timeoutMs,expected:request.expected},{callDir,persistMetadata});
|
|
33
|
+
return invokeNative({...processOptions,auth,binary:codexBinary,workDir:path.join(callDir,'work'),referencePath,referencePaths,prompt,timeoutMs,expected:request.expected},{callDir,persistMetadata});
|
|
33
34
|
},{prepare:async({callDir})=>{
|
|
34
35
|
const authHome=codexHome??process.env.CODEX_HOME??path.join(os.homedir(),'.codex');
|
|
35
36
|
auth=await prepareHome({authHome,binary:codexBinary,runDir:callDir});
|