@enderfga/claw-orchestrator 3.7.1 → 4.0.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 (107) hide show
  1. package/README.md +19 -3
  2. package/dist/bin/cli.js +30 -7
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/src/autoloop/messages.d.ts +0 -2
  5. package/dist/src/autoloop/messages.js +0 -2
  6. package/dist/src/autoloop/messages.js.map +1 -1
  7. package/dist/src/autoloop/planner-tools.d.ts +3 -3
  8. package/dist/src/autoloop/planner-tools.js +3 -3
  9. package/dist/src/autoloop/runner.d.ts +1 -2
  10. package/dist/src/autoloop/runner.js +1 -2
  11. package/dist/src/autoloop/runner.js.map +1 -1
  12. package/dist/src/autoloop/types.d.ts +0 -2
  13. package/dist/src/autoloop/types.js +0 -2
  14. package/dist/src/autoloop/types.js.map +1 -1
  15. package/dist/src/council.js +1 -0
  16. package/dist/src/council.js.map +1 -1
  17. package/dist/src/dashboard/index.html +709 -8
  18. package/dist/src/embedded-server.js +172 -6
  19. package/dist/src/embedded-server.js.map +1 -1
  20. package/dist/src/index.js +222 -1
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/session-manager.d.ts +82 -1
  23. package/dist/src/session-manager.js +262 -5
  24. package/dist/src/session-manager.js.map +1 -1
  25. package/dist/src/ultraapp/build-events.d.ts +46 -0
  26. package/dist/src/ultraapp/build-events.js +6 -0
  27. package/dist/src/ultraapp/build-events.js.map +1 -0
  28. package/dist/src/ultraapp/build.d.ts +39 -0
  29. package/dist/src/ultraapp/build.js +111 -0
  30. package/dist/src/ultraapp/build.js.map +1 -0
  31. package/dist/src/ultraapp/conventions.d.ts +8 -0
  32. package/dist/src/ultraapp/conventions.js +248 -0
  33. package/dist/src/ultraapp/conventions.js.map +1 -0
  34. package/dist/src/ultraapp/council-adapter.d.ts +49 -0
  35. package/dist/src/ultraapp/council-adapter.js +152 -0
  36. package/dist/src/ultraapp/council-adapter.js.map +1 -0
  37. package/dist/src/ultraapp/deploy.d.ts +45 -0
  38. package/dist/src/ultraapp/deploy.js +82 -0
  39. package/dist/src/ultraapp/deploy.js.map +1 -0
  40. package/dist/src/ultraapp/diff-apply.d.ts +15 -0
  41. package/dist/src/ultraapp/diff-apply.js +48 -0
  42. package/dist/src/ultraapp/diff-apply.js.map +1 -0
  43. package/dist/src/ultraapp/docker.d.ts +57 -0
  44. package/dist/src/ultraapp/docker.js +83 -0
  45. package/dist/src/ultraapp/docker.js.map +1 -0
  46. package/dist/src/ultraapp/feedback-classifier.d.ts +24 -0
  47. package/dist/src/ultraapp/feedback-classifier.js +85 -0
  48. package/dist/src/ultraapp/feedback-classifier.js.map +1 -0
  49. package/dist/src/ultraapp/files.d.ts +24 -0
  50. package/dist/src/ultraapp/files.js +79 -0
  51. package/dist/src/ultraapp/files.js.map +1 -0
  52. package/dist/src/ultraapp/fix-on-failure-session.d.ts +23 -0
  53. package/dist/src/ultraapp/fix-on-failure-session.js +48 -0
  54. package/dist/src/ultraapp/fix-on-failure-session.js.map +1 -0
  55. package/dist/src/ultraapp/fix-on-failure.d.ts +39 -0
  56. package/dist/src/ultraapp/fix-on-failure.js +69 -0
  57. package/dist/src/ultraapp/fix-on-failure.js.map +1 -0
  58. package/dist/src/ultraapp/host-strategy.d.ts +57 -0
  59. package/dist/src/ultraapp/host-strategy.js +205 -0
  60. package/dist/src/ultraapp/host-strategy.js.map +1 -0
  61. package/dist/src/ultraapp/interview-parser.d.ts +41 -0
  62. package/dist/src/ultraapp/interview-parser.js +83 -0
  63. package/dist/src/ultraapp/interview-parser.js.map +1 -0
  64. package/dist/src/ultraapp/interview-tools.d.ts +16 -0
  65. package/dist/src/ultraapp/interview-tools.js +36 -0
  66. package/dist/src/ultraapp/interview-tools.js.map +1 -0
  67. package/dist/src/ultraapp/json-patch.d.ts +6 -0
  68. package/dist/src/ultraapp/json-patch.js +78 -0
  69. package/dist/src/ultraapp/json-patch.js.map +1 -0
  70. package/dist/src/ultraapp/lifecycle.d.ts +38 -0
  71. package/dist/src/ultraapp/lifecycle.js +31 -0
  72. package/dist/src/ultraapp/lifecycle.js.map +1 -0
  73. package/dist/src/ultraapp/manager.d.ts +141 -0
  74. package/dist/src/ultraapp/manager.js +661 -0
  75. package/dist/src/ultraapp/manager.js.map +1 -0
  76. package/dist/src/ultraapp/narrator-prompt.d.ts +10 -0
  77. package/dist/src/ultraapp/narrator-prompt.js +54 -0
  78. package/dist/src/ultraapp/narrator-prompt.js.map +1 -0
  79. package/dist/src/ultraapp/narrator.d.ts +49 -0
  80. package/dist/src/ultraapp/narrator.js +83 -0
  81. package/dist/src/ultraapp/narrator.js.map +1 -0
  82. package/dist/src/ultraapp/patcher.d.ts +35 -0
  83. package/dist/src/ultraapp/patcher.js +159 -0
  84. package/dist/src/ultraapp/patcher.js.map +1 -0
  85. package/dist/src/ultraapp/router.d.ts +39 -0
  86. package/dist/src/ultraapp/router.js +139 -0
  87. package/dist/src/ultraapp/router.js.map +1 -0
  88. package/dist/src/ultraapp/spec-delta.d.ts +18 -0
  89. package/dist/src/ultraapp/spec-delta.js +35 -0
  90. package/dist/src/ultraapp/spec-delta.js.map +1 -0
  91. package/dist/src/ultraapp/spec.d.ts +82 -0
  92. package/dist/src/ultraapp/spec.js +125 -0
  93. package/dist/src/ultraapp/spec.js.map +1 -0
  94. package/dist/src/ultraapp/store.d.ts +67 -0
  95. package/dist/src/ultraapp/store.js +177 -0
  96. package/dist/src/ultraapp/store.js.map +1 -0
  97. package/dist/src/ultraapp/versions.d.ts +50 -0
  98. package/dist/src/ultraapp/versions.js +65 -0
  99. package/dist/src/ultraapp/versions.js.map +1 -0
  100. package/openclaw.plugin.json +15 -1
  101. package/package.json +3 -1
  102. package/skills/SKILL.md +2 -2
  103. package/skills/references/autoloop.md +1 -1
  104. package/skills/references/mcp.md +2 -2
  105. package/skills/references/tools.md +183 -0
  106. package/skills/references/ultraapp.md +203 -0
  107. package/skills/ultraapp/SKILL.md +112 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-apply.js","sourceRoot":"","sources":["../../../src/ultraapp/diff-apply.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,GAAW;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAA4C,EAAE,CAAC;IAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,OAAO,KAAK,WAAW,CAAC;QACzC,MAAM,QAAQ,GAAG,OAAO,KAAK,WAAW,CAAC;QACzC,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAEzC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAiB,CAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACpF,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAChE,SAAS;QACX,CAAC;QACD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Thin wrapper around the docker CLI. Centralised so tests mock the
3
+ * spawnFn and the rest of ultraapp doesn't touch child_process directly.
4
+ */
5
+ import { type ChildProcess } from 'node:child_process';
6
+ type SpawnFn = (cmd: string, args: string[]) => ChildProcess;
7
+ export interface BuildArgs {
8
+ tag: string;
9
+ cwd: string;
10
+ buildArgs?: Record<string, string>;
11
+ spawnFn?: SpawnFn;
12
+ }
13
+ export interface BuildResult {
14
+ ok: boolean;
15
+ imageId?: string;
16
+ error?: string;
17
+ }
18
+ export declare function dockerBuild(args: BuildArgs): Promise<BuildResult>;
19
+ export interface RunArgs {
20
+ image: string;
21
+ name: string;
22
+ hostPort: number;
23
+ env: Record<string, string>;
24
+ volumes: Record<string, string>;
25
+ spawnFn?: SpawnFn;
26
+ }
27
+ export interface DockerRunResult {
28
+ ok: boolean;
29
+ containerName?: string;
30
+ error?: string;
31
+ }
32
+ export declare function dockerRun(args: RunArgs): Promise<DockerRunResult>;
33
+ export declare function dockerStop(name: string, spawnFn?: SpawnFn): Promise<{
34
+ ok: boolean;
35
+ error?: string;
36
+ }>;
37
+ export declare function dockerRm(name: string, spawnFn?: SpawnFn): Promise<{
38
+ ok: boolean;
39
+ error?: string;
40
+ }>;
41
+ export declare function dockerStart(name: string, spawnFn?: SpawnFn): Promise<{
42
+ ok: boolean;
43
+ error?: string;
44
+ }>;
45
+ export declare function dockerRmi(tag: string, spawnFn?: SpawnFn): Promise<{
46
+ ok: boolean;
47
+ error?: string;
48
+ }>;
49
+ export interface PsResult {
50
+ containers: Array<{
51
+ name: string;
52
+ state: string;
53
+ ports: string;
54
+ }>;
55
+ }
56
+ export declare function dockerPs(spawnFn?: SpawnFn): Promise<PsResult>;
57
+ export {};
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Thin wrapper around the docker CLI. Centralised so tests mock the
3
+ * spawnFn and the rest of ultraapp doesn't touch child_process directly.
4
+ */
5
+ import { spawn as realSpawn } from 'node:child_process';
6
+ async function run(args, spawnFn) {
7
+ return new Promise((resolve) => {
8
+ const child = spawnFn('docker', args);
9
+ let stdout = '';
10
+ let stderr = '';
11
+ child.stdout?.on('data', (d) => (stdout += d.toString()));
12
+ child.stderr?.on('data', (d) => (stderr += d.toString()));
13
+ child.on('close', (code) => {
14
+ resolve({ ok: code === 0, stdout, stderr, code: code ?? -1 });
15
+ });
16
+ });
17
+ }
18
+ export async function dockerBuild(args) {
19
+ const sp = args.spawnFn ?? realSpawn;
20
+ const cli = ['build', '-t', args.tag];
21
+ for (const [k, v] of Object.entries(args.buildArgs ?? {})) {
22
+ cli.push('--build-arg', `${k}=${v}`);
23
+ }
24
+ cli.push(args.cwd);
25
+ const r = await run(cli, sp);
26
+ if (!r.ok)
27
+ return { ok: false, error: r.stderr || `docker build exited ${r.code}` };
28
+ // Try several patterns. Modern buildx prints "writing image sha256:...";
29
+ // legacy docker prints "Successfully built <hash>".
30
+ const m = /Successfully built ([a-f0-9]+)/.exec(r.stdout) ??
31
+ /writing image sha256:([a-f0-9]+)/.exec(r.stdout) ??
32
+ /writing image sha256:([a-f0-9]+)/.exec(r.stderr);
33
+ return { ok: true, imageId: m?.[1] ?? 'unknown' };
34
+ }
35
+ export async function dockerRun(args) {
36
+ const sp = args.spawnFn ?? realSpawn;
37
+ const cli = ['run', '-d', '--name', args.name, '--restart', 'unless-stopped', '-p', `${args.hostPort}:3000`];
38
+ for (const [k, v] of Object.entries(args.env))
39
+ cli.push('-e', `${k}=${v}`);
40
+ for (const [host, container] of Object.entries(args.volumes))
41
+ cli.push('-v', `${host}:${container}`);
42
+ cli.push(args.image);
43
+ const r = await run(cli, sp);
44
+ if (!r.ok)
45
+ return { ok: false, error: r.stderr || `docker run exited ${r.code}` };
46
+ return { ok: true, containerName: r.stdout.trim() };
47
+ }
48
+ export async function dockerStop(name, spawnFn = realSpawn) {
49
+ const r = await run(['stop', name], spawnFn);
50
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
51
+ }
52
+ export async function dockerRm(name, spawnFn = realSpawn) {
53
+ const r = await run(['rm', '-f', name], spawnFn);
54
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
55
+ }
56
+ export async function dockerStart(name, spawnFn = realSpawn) {
57
+ const r = await run(['start', name], spawnFn);
58
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
59
+ }
60
+ export async function dockerRmi(tag, spawnFn = realSpawn) {
61
+ const r = await run(['rmi', '-f', tag], spawnFn);
62
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
63
+ }
64
+ export async function dockerPs(spawnFn = realSpawn) {
65
+ const r = await run(['ps', '-a', '--format', '{{json .}}'], spawnFn);
66
+ if (!r.ok)
67
+ return { containers: [] };
68
+ const containers = r.stdout
69
+ .split('\n')
70
+ .filter((l) => l.length > 0)
71
+ .map((l) => {
72
+ try {
73
+ const j = JSON.parse(l);
74
+ return { name: j.Names, state: j.State, ports: j.Ports };
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ })
80
+ .filter((x) => x !== null);
81
+ return { containers };
82
+ }
83
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../../src/ultraapp/docker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Done-mode feedback classifier — single Haiku call categorising user input
3
+ * into cosmetic / spec-delta / structural so the manager can route it.
4
+ *
5
+ * Safe-default fallback: if the LLM call fails or returns malformed output,
6
+ * we return spec-delta (cheaper to recover from than wrongly running the
7
+ * patcher on a structural change).
8
+ */
9
+ import type { AppSpec } from './spec.js';
10
+ export interface ClassifyArgs {
11
+ text: string;
12
+ currentSpec: AppSpec;
13
+ language: 'zh' | 'en';
14
+ llmCall: (prompt: string) => Promise<{
15
+ output: string;
16
+ }>;
17
+ }
18
+ export type FeedbackClass = 'cosmetic' | 'spec-delta' | 'structural';
19
+ export interface ClassifyResult {
20
+ class: FeedbackClass;
21
+ reason: string;
22
+ proposedAction: string;
23
+ }
24
+ export declare function classifyFeedback(args: ClassifyArgs): Promise<ClassifyResult>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Done-mode feedback classifier — single Haiku call categorising user input
3
+ * into cosmetic / spec-delta / structural so the manager can route it.
4
+ *
5
+ * Safe-default fallback: if the LLM call fails or returns malformed output,
6
+ * we return spec-delta (cheaper to recover from than wrongly running the
7
+ * patcher on a structural change).
8
+ */
9
+ const SYSTEM = `
10
+ You classify post-deploy user feedback for ultraapp into one of three buckets:
11
+
12
+ cosmetic — visual/UI tweak; doesn't change AppSpec
13
+ (e.g., "button too dark", "logo too small", "use a serif font")
14
+ spec-delta — adds, removes, or changes a step / input / output / runtime field
15
+ (e.g., "also output a thumbnail", "input should accept audio")
16
+ structural — workflow doesn't fit the original AppSpec at all; better to
17
+ start a fresh ultraapp run
18
+ (e.g., "this isn't even the right pipeline", "I want a totally
19
+ different app")
20
+
21
+ Return JSON in a fenced \`\`\`classification block:
22
+
23
+ {
24
+ "class": "cosmetic" | "spec-delta" | "structural",
25
+ "reason": "<one sentence explanation>",
26
+ "proposedAction": "<one phrase: e.g., 'apply patch' / 'focused interview on outputs' / 'suggest new run'>"
27
+ }
28
+
29
+ Be conservative: when in doubt, prefer spec-delta over structural (cheaper to
30
+ recover from). Prefer cosmetic over spec-delta only when you're confident the
31
+ change is purely visual.
32
+ `.trim();
33
+ const FENCE_RE = /```classification\s*\n([\s\S]*?)\n```/;
34
+ export async function classifyFeedback(args) {
35
+ const userPrompt = `Current AppSpec (JSON):
36
+
37
+ \`\`\`json
38
+ ${JSON.stringify(args.currentSpec, null, 2)}
39
+ \`\`\`
40
+
41
+ User feedback (in ${args.language === 'zh' ? 'Chinese' : 'English'}):
42
+
43
+ > ${args.text}
44
+
45
+ Classify it.`;
46
+ let output;
47
+ try {
48
+ const r = await args.llmCall(`${SYSTEM}\n\n${userPrompt}`);
49
+ output = r.output;
50
+ }
51
+ catch (e) {
52
+ return {
53
+ class: 'spec-delta',
54
+ reason: `classifier error: ${e.message} (fallback)`,
55
+ proposedAction: 'focused interview',
56
+ };
57
+ }
58
+ const m = FENCE_RE.exec(output);
59
+ if (!m) {
60
+ return {
61
+ class: 'spec-delta',
62
+ reason: 'malformed classifier output (fallback to spec-delta)',
63
+ proposedAction: 'focused interview',
64
+ };
65
+ }
66
+ try {
67
+ const j = JSON.parse(m[1]);
68
+ if (j.class === 'cosmetic' || j.class === 'spec-delta' || j.class === 'structural') {
69
+ return {
70
+ class: j.class,
71
+ reason: j.reason ?? '',
72
+ proposedAction: j.proposedAction ?? '',
73
+ };
74
+ }
75
+ }
76
+ catch {
77
+ /* fall through */
78
+ }
79
+ return {
80
+ class: 'spec-delta',
81
+ reason: 'malformed classifier JSON (fallback)',
82
+ proposedAction: 'focused interview',
83
+ };
84
+ }
85
+ //# sourceMappingURL=feedback-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-classifier.js","sourceRoot":"","sources":["../../../src/ultraapp/feedback-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmBH,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBd,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,QAAQ,GAAG,uCAAuC,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAkB;IACvD,MAAM,UAAU,GAAG;;;EAGnB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;;;oBAGvB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;;IAE9D,IAAI,CAAC,IAAI;;aAEA,CAAC;IAEZ,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,OAAO,UAAU,EAAE,CAAC,CAAC;QAC3D,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACpB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,qBAAsB,CAAW,CAAC,OAAO,aAAa;YAC9D,cAAc,EAAE,mBAAmB;SACpC,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO;YACL,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,sDAAsD;YAC9D,cAAc,EAAE,mBAAmB;SACpC,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAA4B,CAAC;QACtD,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;YACnF,OAAO;gBACL,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;gBACtB,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,EAAE;aACvC,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,OAAO;QACL,KAAK,EAAE,YAAY;QACnB,MAAM,EAAE,sCAAsC;QAC9C,cAAc,EAAE,mBAAmB;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface PathValidationOptions {
2
+ allow: string[];
3
+ lstat?: (p: string) => {
4
+ isSymbolicLink: () => boolean;
5
+ };
6
+ }
7
+ export declare function validateLocalPath(p: string, opts: PathValidationOptions): void;
8
+ export declare function defaultAllowedRoots(): string[];
9
+ export declare function ingestUpload(examplesDir: string, filename: string, data: Buffer): Promise<string>;
10
+ type RunResult = {
11
+ ok: boolean;
12
+ stdout: string;
13
+ };
14
+ type Runner = (cmd: string, args: string[]) => Promise<RunResult>;
15
+ export interface ExtractMetaOptions {
16
+ runner?: Runner;
17
+ }
18
+ export interface FileMetadata {
19
+ fileType?: string;
20
+ ffprobe?: unknown;
21
+ sizeBytes?: number;
22
+ }
23
+ export declare function extractMetadata(p: string, opts?: ExtractMetaOptions): Promise<FileMetadata>;
24
+ export {};
@@ -0,0 +1,79 @@
1
+ import * as fs from 'node:fs';
2
+ import * as fsp from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import * as crypto from 'node:crypto';
5
+ import { spawn } from 'node:child_process';
6
+ const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
7
+ export function validateLocalPath(p, opts) {
8
+ if (!path.isAbsolute(p))
9
+ throw new Error('path must be absolute');
10
+ if (p.includes('/.') || p.startsWith('.'))
11
+ throw new Error('dotfile/dotdir paths are not allowed');
12
+ const inside = opts.allow.some((root) => p === root || p.startsWith(root + path.sep) || p.startsWith(root + '/'));
13
+ if (!inside)
14
+ throw new Error(`path outside sandbox: ${opts.allow.join(', ')}`);
15
+ const lstat = opts.lstat ?? ((q) => fs.lstatSync(q));
16
+ let info;
17
+ try {
18
+ info = lstat(p);
19
+ }
20
+ catch {
21
+ return;
22
+ }
23
+ if (info.isSymbolicLink())
24
+ throw new Error('symlink paths are not allowed');
25
+ }
26
+ export function defaultAllowedRoots() {
27
+ const home = process.env.HOME ?? '';
28
+ const allow = [];
29
+ if (home)
30
+ allow.push(home);
31
+ allow.push('/tmp');
32
+ return allow;
33
+ }
34
+ export async function ingestUpload(examplesDir, filename, data) {
35
+ if (data.byteLength > MAX_UPLOAD_BYTES) {
36
+ throw new Error(`upload too large (${data.byteLength} bytes, max ${MAX_UPLOAD_BYTES})`);
37
+ }
38
+ await fsp.mkdir(examplesDir, { recursive: true });
39
+ const safe = filename.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 100);
40
+ const ref = path.join(examplesDir, `${crypto.randomUUID()}-${safe}`);
41
+ await fsp.writeFile(ref, data);
42
+ return ref;
43
+ }
44
+ const realRunner = (cmd, args) => new Promise((resolve) => {
45
+ const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'ignore'] });
46
+ let stdout = '';
47
+ child.stdout.on('data', (d) => {
48
+ stdout += d.toString();
49
+ });
50
+ child.on('close', (code) => resolve({ ok: code === 0, stdout }));
51
+ child.on('error', () => resolve({ ok: false, stdout: '' }));
52
+ });
53
+ export async function extractMetadata(p, opts = {}) {
54
+ const runner = opts.runner ?? realRunner;
55
+ const meta = {};
56
+ try {
57
+ const stat = await fsp.stat(p);
58
+ meta.sizeBytes = stat.size;
59
+ }
60
+ catch {
61
+ /* missing file — return empty */
62
+ }
63
+ const fileRes = await runner('file', [p]);
64
+ if (fileRes.ok) {
65
+ const m = fileRes.stdout.trim();
66
+ meta.fileType = m.includes(': ') ? m.slice(m.indexOf(': ') + 2) : m;
67
+ }
68
+ const ffRes = await runner('ffprobe', ['-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', p]);
69
+ if (ffRes.ok && ffRes.stdout.trim().length > 0) {
70
+ try {
71
+ meta.ffprobe = JSON.parse(ffRes.stdout);
72
+ }
73
+ catch {
74
+ /* malformed → skip */
75
+ }
76
+ }
77
+ return meta;
78
+ }
79
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/ultraapp/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface SessionManagerLike {
2
+ startSession(c: {
3
+ name?: string;
4
+ engine?: string;
5
+ model?: string;
6
+ cwd?: string;
7
+ systemPrompt?: string;
8
+ permissionMode?: string;
9
+ }): Promise<{
10
+ name: string;
11
+ }>;
12
+ sendMessage(name: string, msg: string): Promise<{
13
+ output: string;
14
+ }>;
15
+ stopSession(name: string): Promise<void>;
16
+ }
17
+ export interface FixerArgs {
18
+ worktreePath: string;
19
+ failingCommand: string;
20
+ tail: string;
21
+ }
22
+ export declare function spawnFixerSession(args: FixerArgs): Promise<void>;
23
+ export declare function spawnFixerSessionWith(sm: SessionManagerLike, args: FixerArgs): Promise<void>;
@@ -0,0 +1,48 @@
1
+ import * as crypto from 'node:crypto';
2
+ const SYSTEM = `
3
+ You are a fix-on-failure agent for an ultraapp build. The user will give you
4
+ the worktree path, a failing shell command, and the last 200 lines of its
5
+ output. Your job: edit files to make the failing command succeed. Don't change
6
+ application behaviour, only fix mechanical errors (types, imports, dockerfile
7
+ syntax, missing files). When done, reply with the literal marker line:
8
+
9
+ [FIX-ROUND-DONE]
10
+
11
+ If the failure is genuinely caused by a behaviour problem you can't fix
12
+ without changing semantics, reply with:
13
+
14
+ [FIX-ROUND-GIVEUP] reason: <one sentence>
15
+
16
+ Then [FIX-ROUND-DONE].
17
+ `.trim();
18
+ const COMPLETE_RE = /\[FIX-ROUND-DONE\]/;
19
+ const MAX_ATTEMPTS = 5;
20
+ export async function spawnFixerSession(args) {
21
+ const { SessionManager } = await import('../session-manager.js');
22
+ const sm = new SessionManager();
23
+ await spawnFixerSessionWith(sm, args);
24
+ }
25
+ export async function spawnFixerSessionWith(sm, args) {
26
+ const sessionName = `ua-fix-${crypto.randomBytes(4).toString('hex')}`;
27
+ await sm.startSession({
28
+ name: sessionName,
29
+ engine: 'claude',
30
+ model: 'claude-opus-4-7',
31
+ cwd: args.worktreePath,
32
+ systemPrompt: SYSTEM,
33
+ permissionMode: 'bypassPermissions',
34
+ });
35
+ try {
36
+ const prompt = `Failing command: \`${args.failingCommand}\`\n\nLast output:\n\n\`\`\`\n${args.tail}\n\`\`\`\n\nFix it. End with [FIX-ROUND-DONE].`;
37
+ let attempts = 0;
38
+ while (attempts++ < MAX_ATTEMPTS) {
39
+ const r = await sm.sendMessage(sessionName, attempts === 1 ? prompt : 'continue. when done, output [FIX-ROUND-DONE].');
40
+ if (COMPLETE_RE.test(r.output))
41
+ break;
42
+ }
43
+ }
44
+ finally {
45
+ await sm.stopSession(sessionName).catch(() => { });
46
+ }
47
+ }
48
+ //# sourceMappingURL=fix-on-failure-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-on-failure-session.js","sourceRoot":"","sources":["../../../src/ultraapp/fix-on-failure-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAqBtC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;CAed,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAe;IACrD,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,MAAM,qBAAqB,CAAC,EAAmC,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAAsB,EAAE,IAAe;IACjF,MAAM,WAAW,GAAG,UAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,MAAM,EAAE,CAAC,YAAY,CAAC;QACpB,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,iBAAiB;QACxB,GAAG,EAAE,IAAI,CAAC,YAAY;QACtB,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,mBAAmB;KACpC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,sBAAsB,IAAI,CAAC,cAAc,iCAAiC,IAAI,CAAC,IAAI,gDAAgD,CAAC;QACnJ,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,OAAO,QAAQ,EAAE,GAAG,YAAY,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,WAAW,CAC5B,WAAW,EACX,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,+CAA+C,CAC1E,CAAC;YACF,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;gBAAE,MAAM;QACxC,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Purpose-built helper: run the standard verification pipeline in a worktree,
3
+ * spawn a Claude session to fix mechanical errors on red, retry up to N rounds.
4
+ *
5
+ * Not to be confused with src/autoloop/, which is a planner/coder/reviewer
6
+ * message-bus orchestrator for a different problem shape.
7
+ */
8
+ export interface ShellResult {
9
+ ok: boolean;
10
+ stdout: string;
11
+ stderr: string;
12
+ }
13
+ export type ShellRunner = (cmd: string, args: string[], opts: {
14
+ cwd: string;
15
+ }) => Promise<ShellResult>;
16
+ export interface FixerArgs {
17
+ worktreePath: string;
18
+ failingCommand: string;
19
+ tail: string;
20
+ }
21
+ export type FixerSpawner = (args: FixerArgs) => Promise<void>;
22
+ export interface FixOnFailureArgs {
23
+ worktreePath: string;
24
+ maxRounds: number;
25
+ shell?: ShellRunner;
26
+ spawnFixer?: FixerSpawner;
27
+ steps?: Array<{
28
+ cmd: string;
29
+ args: string[];
30
+ required?: boolean;
31
+ }>;
32
+ }
33
+ export interface FixOnFailureResult {
34
+ ok: boolean;
35
+ rounds: number;
36
+ lastError?: string;
37
+ failingCommand?: string;
38
+ }
39
+ export declare function runFixOnFailure(args: FixOnFailureArgs): Promise<FixOnFailureResult>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Purpose-built helper: run the standard verification pipeline in a worktree,
3
+ * spawn a Claude session to fix mechanical errors on red, retry up to N rounds.
4
+ *
5
+ * Not to be confused with src/autoloop/, which is a planner/coder/reviewer
6
+ * message-bus orchestrator for a different problem shape.
7
+ */
8
+ import { spawn } from 'node:child_process';
9
+ const DEFAULT_STEPS = [
10
+ { cmd: 'npm', args: ['install'] },
11
+ { cmd: 'npm', args: ['run', 'build'] },
12
+ { cmd: 'npm', args: ['test'] },
13
+ { cmd: 'docker', args: ['build', '-t', 'ultraapp-fix:test', '.'] },
14
+ ];
15
+ const TAIL_LINES = 200;
16
+ export async function runFixOnFailure(args) {
17
+ const shell = args.shell ?? realShell;
18
+ const spawnFixer = args.spawnFixer ?? defaultSpawnFixer;
19
+ const steps = args.steps ?? DEFAULT_STEPS;
20
+ let rounds = 0;
21
+ while (true) {
22
+ const failure = await runPipelineOnce(shell, args.worktreePath, steps);
23
+ if (failure === null)
24
+ return { ok: true, rounds };
25
+ if (rounds >= args.maxRounds) {
26
+ return {
27
+ ok: false,
28
+ rounds,
29
+ lastError: failure.lastError,
30
+ failingCommand: failure.failingCommand,
31
+ };
32
+ }
33
+ rounds++;
34
+ await spawnFixer({
35
+ worktreePath: args.worktreePath,
36
+ failingCommand: failure.failingCommand,
37
+ tail: failure.tail,
38
+ });
39
+ }
40
+ }
41
+ async function runPipelineOnce(shell, cwd, steps) {
42
+ for (const step of steps) {
43
+ const r = await shell(step.cmd, step.args, { cwd });
44
+ if (!r.ok) {
45
+ const cmdline = `${step.cmd} ${step.args.join(' ')}`;
46
+ const tail = lastN(r.stderr || r.stdout, TAIL_LINES);
47
+ return { failingCommand: cmdline, lastError: tail.slice(0, 500), tail };
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ function lastN(s, n) {
53
+ const lines = s.split('\n');
54
+ return lines.slice(-n).join('\n');
55
+ }
56
+ const realShell = (cmd, args, opts) => new Promise((resolve) => {
57
+ const child = spawn(cmd, args, { cwd: opts.cwd, stdio: ['ignore', 'pipe', 'pipe'] });
58
+ let stdout = '';
59
+ let stderr = '';
60
+ child.stdout.on('data', (d) => (stdout += d.toString()));
61
+ child.stderr.on('data', (d) => (stderr += d.toString()));
62
+ child.on('close', (code) => resolve({ ok: code === 0, stdout, stderr }));
63
+ child.on('error', (e) => resolve({ ok: false, stdout, stderr: stderr + e.message }));
64
+ });
65
+ const defaultSpawnFixer = async (args) => {
66
+ const { spawnFixerSession } = await import('./fix-on-failure-session.js');
67
+ await spawnFixerSession(args);
68
+ };
69
+ //# sourceMappingURL=fix-on-failure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-on-failure.js","sourceRoot":"","sources":["../../../src/ultraapp/fix-on-failure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Host-spawn runtime strategy — runs the generated app as a regular Node
3
+ * process on the host, no Docker. The natural default for personal /
4
+ * internal-tool use; Docker is the opt-in mode for shared production
5
+ * hosts where isolation matters.
6
+ *
7
+ * Mirrors the docker.ts shape (`build / run / stop / start / rm`) so the
8
+ * deploy orchestrator and lifecycle helpers stay the same.
9
+ *
10
+ * State persistence:
11
+ * ~/.claw-orchestrator/host-procs.json keyed by container-name (we keep
12
+ * the term to match the rest of the API) → { pid, port, cwd, env, since }.
13
+ * Survives orchestrator restarts: hostStart re-spawns from the saved
14
+ * metadata, hostStop kills the recorded pid + child group.
15
+ */
16
+ import type { BuildResult, DockerRunResult, PsResult } from './docker.js';
17
+ export interface HostBuildArgs {
18
+ tag: string;
19
+ cwd: string;
20
+ buildArgs?: Record<string, string>;
21
+ }
22
+ /**
23
+ * Host build = `npm install` + `npm run build` (if a build script exists).
24
+ * No isolation; we mutate the worktree directly. The "tag" is metadata only
25
+ * — host-mode doesn't have container images.
26
+ */
27
+ export declare function hostBuild(args: HostBuildArgs): Promise<BuildResult>;
28
+ export interface HostRunArgs {
29
+ image: string;
30
+ name: string;
31
+ hostPort: number;
32
+ env: Record<string, string>;
33
+ volumes?: Record<string, string>;
34
+ }
35
+ /**
36
+ * Host run = spawn `npm start` (or `node server.js` fallback) detached, with
37
+ * PORT + caller env. Records pid in host-procs.json keyed by name.
38
+ */
39
+ export declare function hostRun(args: HostRunArgs): Promise<DockerRunResult>;
40
+ export declare function hostStop(name: string): Promise<{
41
+ ok: boolean;
42
+ error?: string;
43
+ }>;
44
+ export declare function hostStart(name: string): Promise<{
45
+ ok: boolean;
46
+ error?: string;
47
+ }>;
48
+ export declare function hostRm(name: string): Promise<{
49
+ ok: boolean;
50
+ error?: string;
51
+ }>;
52
+ /** Stub matching dockerRmi — host mode has no images, so this is a no-op. */
53
+ export declare function hostRmi(_tag: string): Promise<{
54
+ ok: boolean;
55
+ error?: string;
56
+ }>;
57
+ export declare function hostPs(): Promise<PsResult>;