@enderfga/claw-orchestrator 3.7.1 → 4.0.3

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 (115) hide show
  1. package/README.md +45 -3
  2. package/configs/autoloop-coder-prompt.md +65 -28
  3. package/configs/autoloop-planner-prompt.md +103 -42
  4. package/configs/autoloop-reviewer-prompt.md +58 -30
  5. package/dist/bin/cli.js +30 -7
  6. package/dist/bin/cli.js.map +1 -1
  7. package/dist/src/autoloop/dispatcher.js +16 -2
  8. package/dist/src/autoloop/dispatcher.js.map +1 -1
  9. package/dist/src/autoloop/messages.d.ts +0 -2
  10. package/dist/src/autoloop/messages.js +0 -2
  11. package/dist/src/autoloop/messages.js.map +1 -1
  12. package/dist/src/autoloop/planner-tools.d.ts +11 -6
  13. package/dist/src/autoloop/planner-tools.js +24 -7
  14. package/dist/src/autoloop/planner-tools.js.map +1 -1
  15. package/dist/src/autoloop/runner.d.ts +1 -2
  16. package/dist/src/autoloop/runner.js +1 -2
  17. package/dist/src/autoloop/runner.js.map +1 -1
  18. package/dist/src/autoloop/types.d.ts +0 -2
  19. package/dist/src/autoloop/types.js +0 -2
  20. package/dist/src/autoloop/types.js.map +1 -1
  21. package/dist/src/council.js +1 -0
  22. package/dist/src/council.js.map +1 -1
  23. package/dist/src/dashboard/index.html +1066 -12
  24. package/dist/src/embedded-server.d.ts +7 -0
  25. package/dist/src/embedded-server.js +359 -17
  26. package/dist/src/embedded-server.js.map +1 -1
  27. package/dist/src/index.js +222 -1
  28. package/dist/src/index.js.map +1 -1
  29. package/dist/src/session-manager.d.ts +98 -1
  30. package/dist/src/session-manager.js +340 -5
  31. package/dist/src/session-manager.js.map +1 -1
  32. package/dist/src/ultraapp/build-events.d.ts +46 -0
  33. package/dist/src/ultraapp/build-events.js +6 -0
  34. package/dist/src/ultraapp/build-events.js.map +1 -0
  35. package/dist/src/ultraapp/build.d.ts +39 -0
  36. package/dist/src/ultraapp/build.js +111 -0
  37. package/dist/src/ultraapp/build.js.map +1 -0
  38. package/dist/src/ultraapp/conventions.d.ts +8 -0
  39. package/dist/src/ultraapp/conventions.js +248 -0
  40. package/dist/src/ultraapp/conventions.js.map +1 -0
  41. package/dist/src/ultraapp/council-adapter.d.ts +49 -0
  42. package/dist/src/ultraapp/council-adapter.js +152 -0
  43. package/dist/src/ultraapp/council-adapter.js.map +1 -0
  44. package/dist/src/ultraapp/deploy.d.ts +45 -0
  45. package/dist/src/ultraapp/deploy.js +82 -0
  46. package/dist/src/ultraapp/deploy.js.map +1 -0
  47. package/dist/src/ultraapp/diff-apply.d.ts +15 -0
  48. package/dist/src/ultraapp/diff-apply.js +48 -0
  49. package/dist/src/ultraapp/diff-apply.js.map +1 -0
  50. package/dist/src/ultraapp/docker.d.ts +57 -0
  51. package/dist/src/ultraapp/docker.js +83 -0
  52. package/dist/src/ultraapp/docker.js.map +1 -0
  53. package/dist/src/ultraapp/feedback-classifier.d.ts +24 -0
  54. package/dist/src/ultraapp/feedback-classifier.js +85 -0
  55. package/dist/src/ultraapp/feedback-classifier.js.map +1 -0
  56. package/dist/src/ultraapp/files.d.ts +24 -0
  57. package/dist/src/ultraapp/files.js +79 -0
  58. package/dist/src/ultraapp/files.js.map +1 -0
  59. package/dist/src/ultraapp/fix-on-failure-session.d.ts +23 -0
  60. package/dist/src/ultraapp/fix-on-failure-session.js +48 -0
  61. package/dist/src/ultraapp/fix-on-failure-session.js.map +1 -0
  62. package/dist/src/ultraapp/fix-on-failure.d.ts +39 -0
  63. package/dist/src/ultraapp/fix-on-failure.js +69 -0
  64. package/dist/src/ultraapp/fix-on-failure.js.map +1 -0
  65. package/dist/src/ultraapp/host-strategy.d.ts +57 -0
  66. package/dist/src/ultraapp/host-strategy.js +205 -0
  67. package/dist/src/ultraapp/host-strategy.js.map +1 -0
  68. package/dist/src/ultraapp/interview-parser.d.ts +41 -0
  69. package/dist/src/ultraapp/interview-parser.js +83 -0
  70. package/dist/src/ultraapp/interview-parser.js.map +1 -0
  71. package/dist/src/ultraapp/interview-tools.d.ts +16 -0
  72. package/dist/src/ultraapp/interview-tools.js +36 -0
  73. package/dist/src/ultraapp/interview-tools.js.map +1 -0
  74. package/dist/src/ultraapp/json-patch.d.ts +6 -0
  75. package/dist/src/ultraapp/json-patch.js +78 -0
  76. package/dist/src/ultraapp/json-patch.js.map +1 -0
  77. package/dist/src/ultraapp/lifecycle.d.ts +38 -0
  78. package/dist/src/ultraapp/lifecycle.js +31 -0
  79. package/dist/src/ultraapp/lifecycle.js.map +1 -0
  80. package/dist/src/ultraapp/manager.d.ts +150 -0
  81. package/dist/src/ultraapp/manager.js +684 -0
  82. package/dist/src/ultraapp/manager.js.map +1 -0
  83. package/dist/src/ultraapp/narrator-prompt.d.ts +10 -0
  84. package/dist/src/ultraapp/narrator-prompt.js +54 -0
  85. package/dist/src/ultraapp/narrator-prompt.js.map +1 -0
  86. package/dist/src/ultraapp/narrator.d.ts +49 -0
  87. package/dist/src/ultraapp/narrator.js +83 -0
  88. package/dist/src/ultraapp/narrator.js.map +1 -0
  89. package/dist/src/ultraapp/patcher.d.ts +35 -0
  90. package/dist/src/ultraapp/patcher.js +159 -0
  91. package/dist/src/ultraapp/patcher.js.map +1 -0
  92. package/dist/src/ultraapp/router.d.ts +39 -0
  93. package/dist/src/ultraapp/router.js +139 -0
  94. package/dist/src/ultraapp/router.js.map +1 -0
  95. package/dist/src/ultraapp/spec-delta.d.ts +18 -0
  96. package/dist/src/ultraapp/spec-delta.js +35 -0
  97. package/dist/src/ultraapp/spec-delta.js.map +1 -0
  98. package/dist/src/ultraapp/spec.d.ts +82 -0
  99. package/dist/src/ultraapp/spec.js +125 -0
  100. package/dist/src/ultraapp/spec.js.map +1 -0
  101. package/dist/src/ultraapp/store.d.ts +67 -0
  102. package/dist/src/ultraapp/store.js +177 -0
  103. package/dist/src/ultraapp/store.js.map +1 -0
  104. package/dist/src/ultraapp/versions.d.ts +50 -0
  105. package/dist/src/ultraapp/versions.js +65 -0
  106. package/dist/src/ultraapp/versions.js.map +1 -0
  107. package/openclaw.plugin.json +15 -1
  108. package/package.json +3 -1
  109. package/skills/SKILL.md +2 -2
  110. package/skills/references/autoloop.md +7 -4
  111. package/skills/references/dashboard.md +153 -0
  112. package/skills/references/mcp.md +2 -2
  113. package/skills/references/tools.md +183 -0
  114. package/skills/references/ultraapp.md +203 -0
  115. package/skills/ultraapp/SKILL.md +112 -0
@@ -0,0 +1,205 @@
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 { spawn } from 'node:child_process';
17
+ import * as fs from 'node:fs';
18
+ import * as os from 'node:os';
19
+ import * as path from 'node:path';
20
+ const STATE_FILE = path.join(os.homedir(), '.claw-orchestrator', 'host-procs.json');
21
+ function readState() {
22
+ try {
23
+ return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ function writeState(state) {
30
+ fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true });
31
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
32
+ }
33
+ function isAlive(pid) {
34
+ try {
35
+ process.kill(pid, 0);
36
+ return true;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ /**
43
+ * Host build = `npm install` + `npm run build` (if a build script exists).
44
+ * No isolation; we mutate the worktree directly. The "tag" is metadata only
45
+ * — host-mode doesn't have container images.
46
+ */
47
+ export async function hostBuild(args) {
48
+ if (!fs.existsSync(path.join(args.cwd, 'package.json'))) {
49
+ return { ok: false, error: `no package.json at ${args.cwd}` };
50
+ }
51
+ const install = await runCmd('npm', ['install'], { cwd: args.cwd, env: args.buildArgs });
52
+ if (!install.ok)
53
+ return { ok: false, error: `npm install failed: ${install.stderr.slice(0, 500)}` };
54
+ // Only run build if the script exists.
55
+ const pkg = JSON.parse(fs.readFileSync(path.join(args.cwd, 'package.json'), 'utf8'));
56
+ if (pkg.scripts?.build) {
57
+ const build = await runCmd('npm', ['run', 'build'], { cwd: args.cwd, env: args.buildArgs });
58
+ if (!build.ok)
59
+ return { ok: false, error: `npm run build failed: ${build.stderr.slice(0, 500)}` };
60
+ }
61
+ return { ok: true, imageId: `host:${args.tag}` };
62
+ }
63
+ /**
64
+ * Host run = spawn `npm start` (or `node server.js` fallback) detached, with
65
+ * PORT + caller env. Records pid in host-procs.json keyed by name.
66
+ */
67
+ export async function hostRun(args) {
68
+ // image arg is repurposed to mean "cwd of the codebase" in host mode. The
69
+ // caller (deploy orchestrator) passes `tag` as image; we accept either
70
+ // a tag (which we look up — but we don't have a registry, so we expect
71
+ // the caller passes the cwd directly via image).
72
+ // NOTE: deploy.ts currently passes `tag` not `cwd`. We need the cwd
73
+ // separately — passed via env.HOST_CWD as a contract for host mode.
74
+ const cwd = args.env.HOST_CWD || args.image;
75
+ if (!fs.existsSync(path.join(cwd, 'package.json'))) {
76
+ return { ok: false, error: `no package.json at ${cwd}` };
77
+ }
78
+ // Strip the contract env keys from what gets exported to the child
79
+ const childEnv = { ...process.env, ...args.env };
80
+ delete childEnv.HOST_CWD;
81
+ childEnv.PORT = String(args.hostPort);
82
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
83
+ let cmd;
84
+ let cmdArgs;
85
+ if (pkg.scripts?.start) {
86
+ cmd = 'npm';
87
+ cmdArgs = ['start'];
88
+ }
89
+ else if (pkg.main) {
90
+ cmd = 'node';
91
+ cmdArgs = [pkg.main];
92
+ }
93
+ else {
94
+ return { ok: false, error: 'no scripts.start and no main entry in package.json' };
95
+ }
96
+ const child = spawn(cmd, cmdArgs, {
97
+ cwd,
98
+ env: childEnv,
99
+ detached: true,
100
+ stdio: 'ignore',
101
+ });
102
+ child.unref();
103
+ const pid = child.pid;
104
+ if (typeof pid !== 'number') {
105
+ return { ok: false, error: 'spawn returned no pid' };
106
+ }
107
+ const state = readState();
108
+ state[args.name] = {
109
+ pid,
110
+ port: args.hostPort,
111
+ cwd,
112
+ env: args.env,
113
+ since: new Date().toISOString(),
114
+ };
115
+ writeState(state);
116
+ return { ok: true, containerName: args.name };
117
+ }
118
+ export async function hostStop(name) {
119
+ const state = readState();
120
+ const entry = state[name];
121
+ if (!entry)
122
+ return { ok: false, error: `no host proc named ${name}` };
123
+ if (isAlive(entry.pid)) {
124
+ try {
125
+ // Kill the process group (child + grandchildren via npm)
126
+ process.kill(-entry.pid, 'SIGTERM');
127
+ }
128
+ catch {
129
+ try {
130
+ process.kill(entry.pid, 'SIGTERM');
131
+ }
132
+ catch {
133
+ /* already dead */
134
+ }
135
+ }
136
+ // Give it a beat then SIGKILL if still alive
137
+ await new Promise((r) => setTimeout(r, 250));
138
+ if (isAlive(entry.pid)) {
139
+ try {
140
+ process.kill(-entry.pid, 'SIGKILL');
141
+ }
142
+ catch {
143
+ try {
144
+ process.kill(entry.pid, 'SIGKILL');
145
+ }
146
+ catch {
147
+ /* already dead */
148
+ }
149
+ }
150
+ }
151
+ }
152
+ return { ok: true };
153
+ }
154
+ export async function hostStart(name) {
155
+ const state = readState();
156
+ const entry = state[name];
157
+ if (!entry)
158
+ return { ok: false, error: `no host proc named ${name}` };
159
+ if (isAlive(entry.pid))
160
+ return { ok: true }; // already running
161
+ // Re-spawn using saved metadata
162
+ const r = await hostRun({
163
+ image: entry.cwd,
164
+ name,
165
+ hostPort: entry.port,
166
+ env: { ...entry.env, HOST_CWD: entry.cwd },
167
+ });
168
+ return r.ok ? { ok: true } : { ok: false, error: r.error };
169
+ }
170
+ export async function hostRm(name) {
171
+ await hostStop(name).catch(() => ({ ok: true }));
172
+ const state = readState();
173
+ delete state[name];
174
+ writeState(state);
175
+ return { ok: true };
176
+ }
177
+ /** Stub matching dockerRmi — host mode has no images, so this is a no-op. */
178
+ export async function hostRmi(_tag) {
179
+ return { ok: true };
180
+ }
181
+ export async function hostPs() {
182
+ const state = readState();
183
+ const containers = Object.entries(state).map(([name, entry]) => ({
184
+ name,
185
+ state: isAlive(entry.pid) ? 'running' : 'exited',
186
+ ports: `${entry.port}->${entry.port}`,
187
+ }));
188
+ return { containers };
189
+ }
190
+ function runCmd(cmd, args, opts) {
191
+ return new Promise((resolve) => {
192
+ const child = spawn(cmd, args, {
193
+ cwd: opts.cwd,
194
+ env: { ...process.env, ...(opts.env ?? {}) },
195
+ stdio: ['ignore', 'pipe', 'pipe'],
196
+ });
197
+ let stdout = '';
198
+ let stderr = '';
199
+ child.stdout?.on('data', (d) => (stdout += d.toString()));
200
+ child.stderr?.on('data', (d) => (stderr += d.toString()));
201
+ child.on('close', (code) => resolve({ ok: code === 0, stdout, stderr }));
202
+ child.on('error', (e) => resolve({ ok: false, stdout, stderr: stderr + e.message }));
203
+ });
204
+ }
205
+ //# sourceMappingURL=host-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-strategy.js","sourceRoot":"","sources":["../../../src/ultraapp/host-strategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export interface QuestionEnvelope {
2
+ question: string;
3
+ options: Array<{
4
+ label: string;
5
+ value: string;
6
+ }>;
7
+ recommended: string;
8
+ freeformAccepted: boolean;
9
+ context?: string;
10
+ }
11
+ export interface ToolCall {
12
+ name: string;
13
+ argsRaw: string;
14
+ }
15
+ export type ParsedReply = {
16
+ kind: 'question';
17
+ question: QuestionEnvelope;
18
+ rawText: string;
19
+ } | {
20
+ kind: 'complete';
21
+ summary: string;
22
+ } | {
23
+ /** Tool calls AND a question in the same reply. The manager runs the
24
+ tools, sends the tool_result followup (background), and emits the
25
+ question to the user without waiting for another LLM round-trip. */
26
+ kind: 'tools-and-question';
27
+ toolCalls: ToolCall[];
28
+ question: QuestionEnvelope;
29
+ rawText: string;
30
+ } | {
31
+ kind: 'tools';
32
+ toolCalls: ToolCall[];
33
+ rawText: string;
34
+ } | {
35
+ kind: 'text';
36
+ text: string;
37
+ } | {
38
+ kind: 'error';
39
+ reason: string;
40
+ };
41
+ export declare function parseInterviewReply(reply: string): ParsedReply;
@@ -0,0 +1,83 @@
1
+ const FENCE_RE = /```question\s*\n([\s\S]*?)\n```/;
2
+ const COMPLETE_RE = /^\[INTERVIEW:\s*COMPLETE\]\s*$/m;
3
+ const TOOL_RE = /<tool\s+name="([a-z_]+)"\s*>([\s\S]*?)<\/tool>/g;
4
+ export function parseInterviewReply(reply) {
5
+ const tools = [];
6
+ let m;
7
+ TOOL_RE.lastIndex = 0;
8
+ while ((m = TOOL_RE.exec(reply)) !== null) {
9
+ tools.push({ name: m[1], argsRaw: m[2].trim() });
10
+ }
11
+ // Try to also parse a fenced question — it's common (and previously
12
+ // dropped silently) for Claude to emit tool calls AND the next question
13
+ // in a single reply.
14
+ const parsedQuestion = (() => {
15
+ const fence = FENCE_RE.exec(reply);
16
+ if (!fence)
17
+ return null;
18
+ let parsed;
19
+ try {
20
+ parsed = JSON.parse(fence[1]);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ const v = validateQuestion(parsed);
26
+ return v.ok ? v.value : null;
27
+ })();
28
+ if (tools.length > 0 && parsedQuestion) {
29
+ return { kind: 'tools-and-question', toolCalls: tools, question: parsedQuestion, rawText: reply };
30
+ }
31
+ if (tools.length > 0) {
32
+ return { kind: 'tools', toolCalls: tools, rawText: reply };
33
+ }
34
+ if (COMPLETE_RE.test(reply)) {
35
+ return { kind: 'complete', summary: reply.replace(COMPLETE_RE, '').trim() };
36
+ }
37
+ if (parsedQuestion) {
38
+ return { kind: 'question', question: parsedQuestion, rawText: reply };
39
+ }
40
+ // Re-run the parse to surface a malformed-fence error if a fence was present
41
+ // but its JSON was bad.
42
+ const fence = FENCE_RE.exec(reply);
43
+ if (fence) {
44
+ try {
45
+ JSON.parse(fence[1]);
46
+ return { kind: 'error', reason: 'invalid question envelope' };
47
+ }
48
+ catch (e) {
49
+ return { kind: 'error', reason: `malformed JSON in question fence: ${e.message}` };
50
+ }
51
+ }
52
+ return { kind: 'text', text: reply.trim() };
53
+ }
54
+ function validateQuestion(x) {
55
+ if (!x || typeof x !== 'object')
56
+ return { ok: false, reason: 'envelope is not an object' };
57
+ const o = x;
58
+ if (typeof o.question !== 'string' || o.question.length === 0)
59
+ return { ok: false, reason: 'missing .question' };
60
+ if (!Array.isArray(o.options) || o.options.length < 1)
61
+ return { ok: false, reason: 'missing .options' };
62
+ for (const opt of o.options) {
63
+ if (!opt || typeof opt !== 'object')
64
+ return { ok: false, reason: 'option is not an object' };
65
+ const oo = opt;
66
+ if (typeof oo.label !== 'string' || typeof oo.value !== 'string') {
67
+ return { ok: false, reason: 'option missing label/value' };
68
+ }
69
+ }
70
+ if (typeof o.recommended !== 'string')
71
+ return { ok: false, reason: 'missing .recommended' };
72
+ return {
73
+ ok: true,
74
+ value: {
75
+ question: o.question,
76
+ options: o.options,
77
+ recommended: o.recommended,
78
+ freeformAccepted: o.freeformAccepted === true,
79
+ context: typeof o.context === 'string' ? o.context : undefined,
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=interview-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview-parser.js","sourceRoot":"","sources":["../../../src/ultraapp/interview-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { UltraappStore } from './store.js';
2
+ import type { ToolCall } from './interview-parser.js';
3
+ import type { FileMetadata } from './files.js';
4
+ export interface ToolResult {
5
+ name: string;
6
+ ok: boolean;
7
+ result?: unknown;
8
+ error?: string;
9
+ }
10
+ export interface RunToolsArgs {
11
+ runId: string;
12
+ store: UltraappStore;
13
+ extractMetadata: (ref: string) => Promise<FileMetadata>;
14
+ calls: ToolCall[];
15
+ }
16
+ export declare function runToolCalls(args: RunToolsArgs): Promise<ToolResult[]>;
@@ -0,0 +1,36 @@
1
+ import { applyPatch } from './json-patch.js';
2
+ import { isComplete } from './spec.js';
3
+ export async function runToolCalls(args) {
4
+ const out = [];
5
+ for (const call of args.calls) {
6
+ out.push(await dispatch(call, args));
7
+ }
8
+ return out;
9
+ }
10
+ async function dispatch(call, args) {
11
+ try {
12
+ if (call.name === 'update_spec') {
13
+ const patch = JSON.parse(call.argsRaw);
14
+ const spec = await args.store.readSpec(args.runId);
15
+ const next = applyPatch(spec, patch);
16
+ await args.store.writeSpec(args.runId, next);
17
+ return { name: call.name, ok: true, result: { applied: patch.length } };
18
+ }
19
+ if (call.name === 'extract_metadata') {
20
+ const a = JSON.parse(call.argsRaw);
21
+ if (typeof a.ref !== 'string')
22
+ throw new Error('extract_metadata requires .ref');
23
+ const meta = await args.extractMetadata(a.ref);
24
+ return { name: call.name, ok: true, result: meta };
25
+ }
26
+ if (call.name === 'check_completeness') {
27
+ const spec = await args.store.readSpec(args.runId);
28
+ return { name: call.name, ok: true, result: isComplete(spec) };
29
+ }
30
+ return { name: call.name, ok: false, error: `unknown tool: ${call.name}` };
31
+ }
32
+ catch (e) {
33
+ return { name: call.name, ok: false, error: e.message };
34
+ }
35
+ }
36
+ //# sourceMappingURL=interview-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview-tools.js","sourceRoot":"","sources":["../../../src/ultraapp/interview-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAmBvC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAkB;IACnD,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,IAAc,EAAE,IAAkB;IACxD,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAInC,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACrC,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAmB,CAAC,CAAC;YAC5D,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1E,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAoB,CAAC;YACtD,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACjF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/C,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACrD,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACjE,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IAC7E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC;IACrE,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface PatchOp {
2
+ op: string;
3
+ path: string;
4
+ value?: unknown;
5
+ }
6
+ export declare function applyPatch<T>(doc: T, ops: PatchOp[]): T;
@@ -0,0 +1,78 @@
1
+ export function applyPatch(doc, ops) {
2
+ let next = deepClone(doc);
3
+ for (const op of ops) {
4
+ next = applyOne(next, op);
5
+ }
6
+ return next;
7
+ }
8
+ function applyOne(doc, op) {
9
+ const tokens = parsePath(op.path);
10
+ if (tokens.length === 0) {
11
+ if (op.op === 'replace' || op.op === 'add')
12
+ return op.value;
13
+ throw new Error(`unsupported root op ${op.op}`);
14
+ }
15
+ const last = tokens[tokens.length - 1];
16
+ const parent = walk(doc, tokens.slice(0, -1));
17
+ if (op.op === 'add' || op.op === 'replace') {
18
+ setIn(parent, last, op.value);
19
+ return doc;
20
+ }
21
+ if (op.op === 'remove') {
22
+ removeIn(parent, last);
23
+ return doc;
24
+ }
25
+ throw new Error(`unsupported op ${op.op}`);
26
+ }
27
+ function parsePath(path) {
28
+ if (path === '')
29
+ return [];
30
+ if (!path.startsWith('/'))
31
+ throw new Error(`bad path ${path}`);
32
+ return path
33
+ .slice(1)
34
+ .split('/')
35
+ .map((t) => t.replace(/~1/g, '/').replace(/~0/g, '~'));
36
+ }
37
+ function walk(doc, tokens) {
38
+ let cur = doc;
39
+ for (const t of tokens) {
40
+ if (Array.isArray(cur)) {
41
+ const i = t === '-' ? cur.length : Number(t);
42
+ cur = cur[i];
43
+ }
44
+ else if (cur && typeof cur === 'object') {
45
+ cur = cur[t];
46
+ }
47
+ else {
48
+ throw new Error(`cannot walk into ${typeof cur} at ${t}`);
49
+ }
50
+ }
51
+ return cur;
52
+ }
53
+ function setIn(parent, key, value) {
54
+ if (Array.isArray(parent)) {
55
+ if (key === '-')
56
+ parent.push(value);
57
+ else
58
+ parent.splice(Number(key), 0, value);
59
+ }
60
+ else if (parent && typeof parent === 'object') {
61
+ parent[key] = value;
62
+ }
63
+ else {
64
+ throw new Error(`cannot set into ${typeof parent}`);
65
+ }
66
+ }
67
+ function removeIn(parent, key) {
68
+ if (Array.isArray(parent)) {
69
+ parent.splice(Number(key), 1);
70
+ }
71
+ else if (parent && typeof parent === 'object') {
72
+ delete parent[key];
73
+ }
74
+ }
75
+ function deepClone(x) {
76
+ return JSON.parse(JSON.stringify(x));
77
+ }
78
+ //# sourceMappingURL=json-patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-patch.js","sourceRoot":"","sources":["../../../src/ultraapp/json-patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Container lifecycle helpers — start / stop / delete with router (de)registration,
3
+ * plus cold-eligibility logic for the periodic GC sweeper.
4
+ *
5
+ * The actual GC sweeper itself is wired in v0.4+ (it needs lastAccess
6
+ * accounting from the router, which is not present in v0.3). This module
7
+ * only exposes the pure predicate findColdContainers.
8
+ */
9
+ import { dockerStart, dockerStop, dockerRm } from './docker.js';
10
+ import type { UltraappRouter } from './router.js';
11
+ export interface ContainerLastAccess {
12
+ containerName: string;
13
+ lastAccess: number;
14
+ slug?: string;
15
+ port?: number;
16
+ }
17
+ export declare function findColdContainers(items: ContainerLastAccess[], now: number, days: number): ContainerLastAccess[];
18
+ export interface StartOpts {
19
+ dockerStartFn?: typeof dockerStart;
20
+ }
21
+ export declare function startContainerAndRegister(containerName: string, slug: string, port: number, router: UltraappRouter, opts?: StartOpts): Promise<{
22
+ ok: boolean;
23
+ error?: string;
24
+ }>;
25
+ export interface StopOpts {
26
+ dockerStopFn?: typeof dockerStop;
27
+ }
28
+ export declare function stopContainerAndDeregister(containerName: string, slug: string, router: UltraappRouter, opts?: StopOpts): Promise<{
29
+ ok: boolean;
30
+ error?: string;
31
+ }>;
32
+ export interface DeleteOpts {
33
+ dockerRmFn?: typeof dockerRm;
34
+ }
35
+ export declare function deleteContainerAndDeregister(containerName: string, slug: string, router: UltraappRouter, opts?: DeleteOpts): Promise<{
36
+ ok: boolean;
37
+ error?: string;
38
+ }>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Container lifecycle helpers — start / stop / delete with router (de)registration,
3
+ * plus cold-eligibility logic for the periodic GC sweeper.
4
+ *
5
+ * The actual GC sweeper itself is wired in v0.4+ (it needs lastAccess
6
+ * accounting from the router, which is not present in v0.3). This module
7
+ * only exposes the pure predicate findColdContainers.
8
+ */
9
+ import { dockerStart, dockerStop, dockerRm } from './docker.js';
10
+ export function findColdContainers(items, now, days) {
11
+ const cutoff = now - days * 86400000;
12
+ return items.filter((i) => i.lastAccess < cutoff);
13
+ }
14
+ export async function startContainerAndRegister(containerName, slug, port, router, opts = {}) {
15
+ const fn = opts.dockerStartFn ?? dockerStart;
16
+ const r = await fn(containerName);
17
+ if (r.ok)
18
+ router.register(slug, port);
19
+ return r;
20
+ }
21
+ export async function stopContainerAndDeregister(containerName, slug, router, opts = {}) {
22
+ const fn = opts.dockerStopFn ?? dockerStop;
23
+ router.deregister(slug);
24
+ return fn(containerName);
25
+ }
26
+ export async function deleteContainerAndDeregister(containerName, slug, router, opts = {}) {
27
+ const fn = opts.dockerRmFn ?? dockerRm;
28
+ router.deregister(slug);
29
+ return fn(containerName);
30
+ }
31
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/ultraapp/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAUhE,MAAM,UAAU,kBAAkB,CAAC,KAA4B,EAAE,GAAW,EAAE,IAAY;IACxF,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,QAAQ,CAAC;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;AACpD,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,aAAqB,EACrB,IAAY,EACZ,IAAY,EACZ,MAAsB,EACtB,OAAkB,EAAE;IAEpB,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,IAAI,WAAW,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,CAAC;IAClC,IAAI,CAAC,CAAC,EAAE;QAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC;AACX,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,aAAqB,EACrB,IAAY,EACZ,MAAsB,EACtB,OAAiB,EAAE;IAEnB,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,IAAI,UAAU,CAAC;IAC3C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,EAAE,CAAC,aAAa,CAAC,CAAC;AAC3B,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,aAAqB,EACrB,IAAY,EACZ,MAAsB,EACtB,OAAmB,EAAE;IAErB,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC;IACvC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,EAAE,CAAC,aAAa,CAAC,CAAC;AAC3B,CAAC"}