@bubblebrain-ai/bubble 0.0.13 → 0.0.15

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 (80) hide show
  1. package/dist/agent/execution-governor.js +1 -1
  2. package/dist/agent/tool-intent.js +1 -0
  3. package/dist/agent.d.ts +2 -0
  4. package/dist/agent.js +589 -316
  5. package/dist/approval/controller.d.ts +1 -0
  6. package/dist/approval/controller.js +20 -3
  7. package/dist/approval/tool-helper.js +2 -0
  8. package/dist/approval/types.d.ts +14 -1
  9. package/dist/cli.d.ts +3 -1
  10. package/dist/cli.js +12 -0
  11. package/dist/context/compact.js +9 -3
  12. package/dist/context/projector.js +27 -12
  13. package/dist/debug-trace.d.ts +27 -0
  14. package/dist/debug-trace.js +385 -0
  15. package/dist/feishu/agent-host/approval-card.js +9 -0
  16. package/dist/feishu/serve.js +7 -1
  17. package/dist/main.js +41 -0
  18. package/dist/model-catalog.js +1 -0
  19. package/dist/orchestrator/default-hooks.js +19 -8
  20. package/dist/orchestrator/hooks.d.ts +1 -0
  21. package/dist/prompt/environment.js +2 -0
  22. package/dist/prompt/reminders.d.ts +5 -6
  23. package/dist/prompt/reminders.js +8 -9
  24. package/dist/prompt/runtime.js +2 -2
  25. package/dist/provider-openai-codex.d.ts +7 -0
  26. package/dist/provider-openai-codex.js +265 -124
  27. package/dist/provider-registry.d.ts +2 -0
  28. package/dist/provider-registry.js +58 -9
  29. package/dist/provider.d.ts +3 -0
  30. package/dist/provider.js +5 -1
  31. package/dist/session-log.js +13 -1
  32. package/dist/slash-commands/commands.js +12 -0
  33. package/dist/slash-commands/types.d.ts +2 -0
  34. package/dist/stats/usage.d.ts +52 -0
  35. package/dist/stats/usage.js +414 -0
  36. package/dist/tools/apply-patch.d.ts +9 -0
  37. package/dist/tools/apply-patch.js +330 -0
  38. package/dist/tools/bash.js +205 -44
  39. package/dist/tools/edit-apply.d.ts +5 -2
  40. package/dist/tools/edit-apply.js +221 -31
  41. package/dist/tools/edit.js +12 -3
  42. package/dist/tools/file-mutation-queue.d.ts +1 -0
  43. package/dist/tools/file-mutation-queue.js +12 -1
  44. package/dist/tools/index.d.ts +2 -0
  45. package/dist/tools/index.js +7 -1
  46. package/dist/tools/patch-apply.d.ts +41 -0
  47. package/dist/tools/patch-apply.js +312 -0
  48. package/dist/tools/server-manager.d.ts +36 -0
  49. package/dist/tools/server-manager.js +234 -0
  50. package/dist/tools/server.d.ts +6 -0
  51. package/dist/tools/server.js +245 -0
  52. package/dist/tools/write.d.ts +3 -6
  53. package/dist/tools/write.js +26 -46
  54. package/dist/tui/display-history.d.ts +1 -0
  55. package/dist/tui/display-history.js +5 -4
  56. package/dist/tui/edit-diff.js +6 -1
  57. package/dist/tui/model-picker-data.d.ts +10 -0
  58. package/dist/tui/model-picker-data.js +32 -0
  59. package/dist/tui/run.d.ts +2 -0
  60. package/dist/tui/run.js +717 -122
  61. package/dist/tui/tool-renderers/fallback.js +1 -1
  62. package/dist/tui/tool-renderers/write-preview.js +2 -0
  63. package/dist/tui/trace-groups.js +10 -3
  64. package/dist/tui-ink/app.js +1 -4
  65. package/dist/tui-ink/approval/approval-dialog.js +7 -1
  66. package/dist/tui-ink/display-history.d.ts +1 -0
  67. package/dist/tui-ink/display-history.js +5 -4
  68. package/dist/tui-ink/message-list.js +14 -8
  69. package/dist/tui-ink/trace-groups.js +1 -1
  70. package/dist/tui-opentui/app.js +2 -0
  71. package/dist/tui-opentui/approval/approval-dialog.js +7 -1
  72. package/dist/tui-opentui/display-history.d.ts +1 -0
  73. package/dist/tui-opentui/display-history.js +5 -4
  74. package/dist/tui-opentui/edit-diff.js +6 -1
  75. package/dist/tui-opentui/message-list.js +6 -3
  76. package/dist/tui-opentui/trace-groups.js +10 -3
  77. package/dist/types.d.ts +12 -2
  78. package/dist/update/index.d.ts +46 -0
  79. package/dist/update/index.js +240 -0
  80. package/package.json +1 -1
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Self-update: `bubble update` / `bubble upgrade`, plus a cached startup
3
+ * "update available" check.
4
+ *
5
+ * Bubble ships as the npm package `@bubblebrain-ai/bubble`. Updating just means
6
+ * re-installing it globally with whatever package manager put it there, so we
7
+ * detect the install method and run the matching command.
8
+ */
9
+ import { spawn } from "node:child_process";
10
+ import { createRequire } from "node:module";
11
+ import { fileURLToPath } from "node:url";
12
+ import { realpathSync } from "node:fs";
13
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
14
+ import { dirname, join } from "node:path";
15
+ import { getBubbleHome } from "../bubble-home.js";
16
+ const require = createRequire(import.meta.url);
17
+ export const PACKAGE_NAME = "@bubblebrain-ai/bubble";
18
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
19
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // once a day
20
+ export function getCurrentVersion() {
21
+ try {
22
+ const pkg = require("../../package.json");
23
+ return pkg.version ?? "0.0.0";
24
+ }
25
+ catch {
26
+ return "0.0.0";
27
+ }
28
+ }
29
+ export async function fetchLatestVersion(timeoutMs = 5000) {
30
+ try {
31
+ const controller = new AbortController();
32
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
33
+ try {
34
+ const res = await fetch(REGISTRY_URL, {
35
+ signal: controller.signal,
36
+ headers: { accept: "application/json" },
37
+ });
38
+ if (!res.ok)
39
+ return null;
40
+ const data = (await res.json());
41
+ return data.version ?? null;
42
+ }
43
+ finally {
44
+ clearTimeout(timer);
45
+ }
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * Compare two semver-ish strings. Returns 1 if a > b, -1 if a < b, 0 if equal.
53
+ * Handles `x.y.z` and a single pre-release tag (`x.y.z-beta.1`); a release
54
+ * always outranks a pre-release of the same numeric version.
55
+ */
56
+ export function compareVersions(a, b) {
57
+ const parse = (v) => {
58
+ const cleaned = v.trim().replace(/^v/, "");
59
+ const [core, pre] = cleaned.split("-", 2);
60
+ const nums = core.split(".").map((n) => parseInt(n, 10) || 0);
61
+ while (nums.length < 3)
62
+ nums.push(0);
63
+ return { nums, pre: pre ?? "" };
64
+ };
65
+ const pa = parse(a);
66
+ const pb = parse(b);
67
+ for (let i = 0; i < 3; i++) {
68
+ if (pa.nums[i] > pb.nums[i])
69
+ return 1;
70
+ if (pa.nums[i] < pb.nums[i])
71
+ return -1;
72
+ }
73
+ // Equal numeric core: no pre-release beats a pre-release.
74
+ if (pa.pre === pb.pre)
75
+ return 0;
76
+ if (!pa.pre)
77
+ return 1;
78
+ if (!pb.pre)
79
+ return -1;
80
+ return pa.pre > pb.pre ? 1 : -1;
81
+ }
82
+ /**
83
+ * Figure out how this copy of Bubble was installed by inspecting the real path
84
+ * of the package directory (two levels up from this module: dist/update -> pkg).
85
+ */
86
+ export function detectInstall() {
87
+ const rawRoot = fileURLToPath(new URL("../../", import.meta.url));
88
+ let installPath = rawRoot;
89
+ try {
90
+ installPath = realpathSync(rawRoot);
91
+ }
92
+ catch {
93
+ // keep rawRoot
94
+ }
95
+ const lower = installPath.replace(/\\/g, "/").toLowerCase();
96
+ const isUnderNodeModules = lower.includes("/node_modules/");
97
+ // A dev/source checkout has src/ alongside dist/ and isn't under node_modules.
98
+ const isLocalCheckout = !isUnderNodeModules;
99
+ let manager = "unknown";
100
+ if (lower.includes("/cellar/") || lower.includes("/homebrew/")) {
101
+ manager = "homebrew";
102
+ }
103
+ else if (lower.includes("/.bun/") || lower.includes("/bun/install/")) {
104
+ manager = "bun";
105
+ }
106
+ else if (lower.includes("/pnpm/") || lower.includes("/.pnpm/")) {
107
+ manager = "pnpm";
108
+ }
109
+ else if (lower.includes("/.yarn/") || lower.includes("/yarn/global")) {
110
+ manager = "yarn";
111
+ }
112
+ else if (isUnderNodeModules) {
113
+ manager = "npm";
114
+ }
115
+ return { manager, isGlobal: isUnderNodeModules, isLocalCheckout, installPath };
116
+ }
117
+ export function upgradeCommandFor(manager) {
118
+ const spec = `${PACKAGE_NAME}@latest`;
119
+ switch (manager) {
120
+ case "npm":
121
+ case "unknown": // default to npm
122
+ return { cmd: "npm", args: ["install", "-g", spec] };
123
+ case "bun":
124
+ return { cmd: "bun", args: ["add", "-g", spec] };
125
+ case "pnpm":
126
+ return { cmd: "pnpm", args: ["add", "-g", spec] };
127
+ case "yarn":
128
+ return { cmd: "yarn", args: ["global", "add", spec] };
129
+ case "homebrew":
130
+ return null; // handled separately (brew upgrade)
131
+ }
132
+ }
133
+ function spawnInherit(cmd, args) {
134
+ return new Promise((resolve) => {
135
+ const child = spawn(cmd, args, { stdio: "inherit", env: process.env });
136
+ child.on("error", () => resolve(127));
137
+ child.on("exit", (code) => resolve(code ?? 1));
138
+ });
139
+ }
140
+ /**
141
+ * `bubble update` entry point. Returns a process exit code.
142
+ */
143
+ export async function runUpdateCommand(opts = {}) {
144
+ const current = getCurrentVersion();
145
+ process.stdout.write(`Bubble v${current}\n`);
146
+ process.stdout.write("Checking npm for the latest version…\n");
147
+ const latest = await fetchLatestVersion();
148
+ if (!latest) {
149
+ process.stderr.write("Could not reach the npm registry. Check your connection and try again.\n");
150
+ return 1;
151
+ }
152
+ if (compareVersions(latest, current) <= 0) {
153
+ process.stdout.write(`You're already on the latest version (v${current}).\n`);
154
+ return 0;
155
+ }
156
+ process.stdout.write(`Update available: v${current} → v${latest}\n`);
157
+ if (opts.checkOnly) {
158
+ process.stdout.write("Run `bubble update` to install it.\n");
159
+ return 0;
160
+ }
161
+ const info = detectInstall();
162
+ if (info.isLocalCheckout) {
163
+ process.stderr.write("This looks like a local/development checkout, not a global install.\n" +
164
+ "Update it with:\n git pull && npm run build\n");
165
+ return 1;
166
+ }
167
+ if (info.manager === "homebrew") {
168
+ process.stderr.write("Bubble was installed via Homebrew. Update it with:\n brew upgrade bubble\n");
169
+ return 1;
170
+ }
171
+ const command = upgradeCommandFor(info.manager);
172
+ if (!command) {
173
+ process.stderr.write(`Couldn't determine how to update automatically. Run:\n npm install -g ${PACKAGE_NAME}@latest\n`);
174
+ return 1;
175
+ }
176
+ process.stdout.write(`\nUpdating via ${command.cmd}…\n\n`);
177
+ const code = await spawnInherit(command.cmd, command.args);
178
+ if (code === 0) {
179
+ process.stdout.write(`\n✓ Updated to v${latest}. Restart bubble to use the new version.\n`);
180
+ return 0;
181
+ }
182
+ process.stderr.write(`\nUpdate failed (exit ${code}). Try running it manually:\n ${command.cmd} ${command.args.join(" ")}\n` +
183
+ "If this is a permissions error, you may need elevated privileges or to fix your global install prefix.\n");
184
+ return code;
185
+ }
186
+ function cacheFile() {
187
+ return join(getBubbleHome(), "update-check.json");
188
+ }
189
+ async function readCache() {
190
+ try {
191
+ const raw = await readFile(cacheFile(), "utf8");
192
+ const data = JSON.parse(raw);
193
+ if (typeof data.lastCheck === "number" && typeof data.latest === "string") {
194
+ return { lastCheck: data.lastCheck, latest: data.latest };
195
+ }
196
+ return null;
197
+ }
198
+ catch {
199
+ return null;
200
+ }
201
+ }
202
+ async function writeCache(cache) {
203
+ try {
204
+ const file = cacheFile();
205
+ await mkdir(dirname(file), { recursive: true });
206
+ await writeFile(file, JSON.stringify(cache), "utf8");
207
+ }
208
+ catch {
209
+ // best-effort; never fail startup over a cache write
210
+ }
211
+ }
212
+ async function refreshCacheInBackground(now) {
213
+ const latest = await fetchLatestVersion(4000);
214
+ if (latest) {
215
+ await writeCache({ lastCheck: now, latest });
216
+ }
217
+ }
218
+ /**
219
+ * Returns a one-line "update available" notice if the cached latest version is
220
+ * newer than the running one. Reads only a local cache file (fast, no network
221
+ * on the hot path); a stale cache triggers a fire-and-forget refresh so the
222
+ * next launch is accurate. Never throws.
223
+ */
224
+ export async function getStartupUpdateNotice() {
225
+ try {
226
+ const current = getCurrentVersion();
227
+ const now = Date.now();
228
+ const cache = await readCache();
229
+ if (!cache || now - cache.lastCheck > CHECK_INTERVAL_MS) {
230
+ void refreshCacheInBackground(now);
231
+ }
232
+ if (cache && compareVersions(cache.latest, current) > 0) {
233
+ return `Update available: v${current} → v${cache.latest} · run \`bubble update\``;
234
+ }
235
+ return null;
236
+ }
237
+ catch {
238
+ return null;
239
+ }
240
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "A terminal coding agent",
5
5
  "type": "module",
6
6
  "engines": {