codebay 0.4.0 → 0.5.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.
@@ -5,8 +5,8 @@
5
5
  "componentEntryUrls": {
6
6
  "AppShell_2rtnxn6pgp2rh": "/_mochi/client/_hydrate-AppShell_2rtnxn6pgp2rh-f5rzyjm8.js",
7
7
  "InstanceView_2ma45l9bybfhl": "/_mochi/client/_hydrate-InstanceView_2ma45l9bybfhl-nv31ptdp.js",
8
- "AvatarsView_257s9yq1wwr4f": "/_mochi/client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js",
9
- "SettingsView_8hmarwmf8onq": "/_mochi/client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js"
8
+ "SettingsView_8hmarwmf8onq": "/_mochi/client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js",
9
+ "AvatarsView_257s9yq1wwr4f": "/_mochi/client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js"
10
10
  },
11
11
  "cssFileUrls": {
12
12
  "$mochi/templates/DefaultError.svelte": "/_mochi/css/DefaultError-2b0daamydxgb2.css",
@@ -14,35 +14,35 @@
14
14
  "$mochi/templates/BackLink.svelte": "/_mochi/css/BackLink-2wlu0eovz9j86.css",
15
15
  "src/components/AppShell.svelte": "/_mochi/css/AppShell-36mpg5smnbmuw.css",
16
16
  "src/components/InstanceView.svelte": "/_mochi/css/InstanceView-wq85bxtusont.css",
17
- "src/components/AvatarsView.svelte": "/_mochi/css/AvatarsView-3hq37u4dgtljn.css",
18
17
  "src/components/SettingsView.svelte": "/_mochi/css/SettingsView-3uycxa8z548m4.css",
18
+ "src/components/AvatarsView.svelte": "/_mochi/css/AvatarsView-3hq37u4dgtljn.css",
19
19
  "src/components/IdeBar.svelte": "/_mochi/css/IdeBar-2qoqi2ya9zdn3.css",
20
20
  "src/components/IdeLoader.svelte": "/_mochi/css/IdeLoader-26rlko8e00j3g.css",
21
21
  "src/components/DashboardView.svelte": "/_mochi/css/DashboardView-1cy4xpqhja9sx.css",
22
22
  "node_modules/svelte-french-toast/dist/components/CheckmarkIcon.svelte": "/_mochi/css/CheckmarkIcon-3qlazj2oc50jc.css",
23
23
  "node_modules/svelte-french-toast/dist/components/ToastIcon.svelte": "/_mochi/css/ToastIcon-2z5wrkc95khg0.css",
24
- "node_modules/svelte-french-toast/dist/components/LoaderIcon.svelte": "/_mochi/css/LoaderIcon-soq5wt3p05ye.css",
24
+ "node_modules/svelte-french-toast/dist/components/Toaster.svelte": "/_mochi/css/Toaster-kk60j193854n.css",
25
25
  "node_modules/svelte-french-toast/dist/components/ErrorIcon.svelte": "/_mochi/css/ErrorIcon-2x0tzsqxskw28.css",
26
+ "node_modules/svelte-french-toast/dist/components/LoaderIcon.svelte": "/_mochi/css/LoaderIcon-soq5wt3p05ye.css",
26
27
  "node_modules/svelte-french-toast/dist/components/ToastBar.svelte": "/_mochi/css/ToastBar-3havgkmtofntq.css",
27
- "node_modules/svelte-french-toast/dist/components/Toaster.svelte": "/_mochi/css/Toaster-kk60j193854n.css",
28
28
  "src/components/Skeleton.svelte": "/_mochi/css/Skeleton-8k69usiju43h.css",
29
29
  "src/components/StatusBadge.svelte": "/_mochi/css/StatusBadge-3mq0j7z6aq4j.css",
30
30
  "src/components/HealthBox.svelte": "/_mochi/css/HealthBox-er2her0jukwl.css",
31
31
  "src/components/AppBar.svelte": "/_mochi/css/AppBar-3bxinv4pabv8l.css",
32
- "src/components/AvatarEditor.svelte": "/_mochi/css/AvatarEditor-1ryrs1md5g36y.css",
33
32
  "src/components/Avatar.svelte": "/_mochi/css/Avatar-24th63awm9mna.css",
34
33
  "src/components/Button.svelte": "/_mochi/css/Button-2vtbrzb5kgu25.css",
35
- "src/components/CoinButton.svelte": "/_mochi/css/CoinButton-1rej3sh3tkjud.css",
36
34
  "src/components/ThemePicker.svelte": "/_mochi/css/ThemePicker-1ir8jyocm8mvi.css",
37
- "node_modules/svelte-french-toast/dist/components/ToastWrapper.svelte": "/_mochi/css/ToastWrapper-1fbnym10rm2nv.css",
35
+ "src/components/CoinButton.svelte": "/_mochi/css/CoinButton-1rej3sh3tkjud.css",
36
+ "src/components/AvatarEditor.svelte": "/_mochi/css/AvatarEditor-1ryrs1md5g36y.css",
37
+ "src/components/FolderBrowser.svelte": "/_mochi/css/FolderBrowser-1x428zb3f2kti.css",
38
38
  "node_modules/svelte-french-toast/dist/components/ToastMessage.svelte": "/_mochi/css/ToastMessage-2jfwb2og2lfa8.css",
39
+ "node_modules/svelte-french-toast/dist/components/ToastWrapper.svelte": "/_mochi/css/ToastWrapper-1fbnym10rm2nv.css",
39
40
  "src/components/TopBar.svelte": "/_mochi/css/TopBar-2q87li6yt8ids.css",
40
41
  "src/components/InstanceCard.svelte": "/_mochi/css/InstanceCard-16f77aysm7lgp.css",
41
- "src/components/FolderBrowser.svelte": "/_mochi/css/FolderBrowser-1x428zb3f2kti.css",
42
- "src/components/CredMenu.svelte": "/_mochi/css/CredMenu-9hwgeptp7crb.css",
43
- "src/components/SettingsCog.svelte": "/_mochi/css/SettingsCog-jftyadvo64qo.css",
44
42
  "src/components/Brand.svelte": "/_mochi/css/Brand-1bh2b800g5vrn.css",
43
+ "src/components/CredMenu.svelte": "/_mochi/css/CredMenu-9hwgeptp7crb.css",
45
44
  "src/components/BranchBox.svelte": "/_mochi/css/BranchBox-1yqu1q2mgnjwz.css",
45
+ "src/components/SettingsCog.svelte": "/_mochi/css/SettingsCog-jftyadvo64qo.css",
46
46
  "src/components/PortsBox.svelte": "/_mochi/css/PortsBox-1t25b5yg7jva3.css"
47
47
  },
48
48
  "clientFiles": {
@@ -51,35 +51,35 @@
51
51
  "/_mochi/css/BackLink-2wlu0eovz9j86.css": "svelte-css/BackLink-2wlu0eovz9j86.css",
52
52
  "/_mochi/css/AppShell-36mpg5smnbmuw.css": "svelte-css/AppShell-36mpg5smnbmuw.css",
53
53
  "/_mochi/css/InstanceView-wq85bxtusont.css": "svelte-css/InstanceView-wq85bxtusont.css",
54
- "/_mochi/css/AvatarsView-3hq37u4dgtljn.css": "svelte-css/AvatarsView-3hq37u4dgtljn.css",
55
54
  "/_mochi/css/SettingsView-3uycxa8z548m4.css": "svelte-css/SettingsView-3uycxa8z548m4.css",
55
+ "/_mochi/css/AvatarsView-3hq37u4dgtljn.css": "svelte-css/AvatarsView-3hq37u4dgtljn.css",
56
56
  "/_mochi/css/IdeBar-2qoqi2ya9zdn3.css": "svelte-css/IdeBar-2qoqi2ya9zdn3.css",
57
57
  "/_mochi/css/IdeLoader-26rlko8e00j3g.css": "svelte-css/IdeLoader-26rlko8e00j3g.css",
58
58
  "/_mochi/css/DashboardView-1cy4xpqhja9sx.css": "svelte-css/DashboardView-1cy4xpqhja9sx.css",
59
59
  "/_mochi/css/CheckmarkIcon-3qlazj2oc50jc.css": "svelte-css/CheckmarkIcon-3qlazj2oc50jc.css",
60
60
  "/_mochi/css/ToastIcon-2z5wrkc95khg0.css": "svelte-css/ToastIcon-2z5wrkc95khg0.css",
61
- "/_mochi/css/LoaderIcon-soq5wt3p05ye.css": "svelte-css/LoaderIcon-soq5wt3p05ye.css",
61
+ "/_mochi/css/Toaster-kk60j193854n.css": "svelte-css/Toaster-kk60j193854n.css",
62
62
  "/_mochi/css/ErrorIcon-2x0tzsqxskw28.css": "svelte-css/ErrorIcon-2x0tzsqxskw28.css",
63
+ "/_mochi/css/LoaderIcon-soq5wt3p05ye.css": "svelte-css/LoaderIcon-soq5wt3p05ye.css",
63
64
  "/_mochi/css/ToastBar-3havgkmtofntq.css": "svelte-css/ToastBar-3havgkmtofntq.css",
64
- "/_mochi/css/Toaster-kk60j193854n.css": "svelte-css/Toaster-kk60j193854n.css",
65
65
  "/_mochi/css/Skeleton-8k69usiju43h.css": "svelte-css/Skeleton-8k69usiju43h.css",
66
66
  "/_mochi/css/StatusBadge-3mq0j7z6aq4j.css": "svelte-css/StatusBadge-3mq0j7z6aq4j.css",
67
67
  "/_mochi/css/HealthBox-er2her0jukwl.css": "svelte-css/HealthBox-er2her0jukwl.css",
68
68
  "/_mochi/css/AppBar-3bxinv4pabv8l.css": "svelte-css/AppBar-3bxinv4pabv8l.css",
69
- "/_mochi/css/AvatarEditor-1ryrs1md5g36y.css": "svelte-css/AvatarEditor-1ryrs1md5g36y.css",
70
69
  "/_mochi/css/Avatar-24th63awm9mna.css": "svelte-css/Avatar-24th63awm9mna.css",
71
70
  "/_mochi/css/Button-2vtbrzb5kgu25.css": "svelte-css/Button-2vtbrzb5kgu25.css",
72
- "/_mochi/css/CoinButton-1rej3sh3tkjud.css": "svelte-css/CoinButton-1rej3sh3tkjud.css",
73
71
  "/_mochi/css/ThemePicker-1ir8jyocm8mvi.css": "svelte-css/ThemePicker-1ir8jyocm8mvi.css",
74
- "/_mochi/css/ToastWrapper-1fbnym10rm2nv.css": "svelte-css/ToastWrapper-1fbnym10rm2nv.css",
72
+ "/_mochi/css/CoinButton-1rej3sh3tkjud.css": "svelte-css/CoinButton-1rej3sh3tkjud.css",
73
+ "/_mochi/css/AvatarEditor-1ryrs1md5g36y.css": "svelte-css/AvatarEditor-1ryrs1md5g36y.css",
74
+ "/_mochi/css/FolderBrowser-1x428zb3f2kti.css": "svelte-css/FolderBrowser-1x428zb3f2kti.css",
75
75
  "/_mochi/css/ToastMessage-2jfwb2og2lfa8.css": "svelte-css/ToastMessage-2jfwb2og2lfa8.css",
76
+ "/_mochi/css/ToastWrapper-1fbnym10rm2nv.css": "svelte-css/ToastWrapper-1fbnym10rm2nv.css",
76
77
  "/_mochi/css/TopBar-2q87li6yt8ids.css": "svelte-css/TopBar-2q87li6yt8ids.css",
77
78
  "/_mochi/css/InstanceCard-16f77aysm7lgp.css": "svelte-css/InstanceCard-16f77aysm7lgp.css",
78
- "/_mochi/css/FolderBrowser-1x428zb3f2kti.css": "svelte-css/FolderBrowser-1x428zb3f2kti.css",
79
- "/_mochi/css/CredMenu-9hwgeptp7crb.css": "svelte-css/CredMenu-9hwgeptp7crb.css",
80
- "/_mochi/css/SettingsCog-jftyadvo64qo.css": "svelte-css/SettingsCog-jftyadvo64qo.css",
81
79
  "/_mochi/css/Brand-1bh2b800g5vrn.css": "svelte-css/Brand-1bh2b800g5vrn.css",
80
+ "/_mochi/css/CredMenu-9hwgeptp7crb.css": "svelte-css/CredMenu-9hwgeptp7crb.css",
82
81
  "/_mochi/css/BranchBox-1yqu1q2mgnjwz.css": "svelte-css/BranchBox-1yqu1q2mgnjwz.css",
82
+ "/_mochi/css/SettingsCog-jftyadvo64qo.css": "svelte-css/SettingsCog-jftyadvo64qo.css",
83
83
  "/_mochi/css/PortsBox-1t25b5yg7jva3.css": "svelte-css/PortsBox-1t25b5yg7jva3.css",
84
84
  "/_mochi/import-css/index-0nwv3654.css": "import-css/index-0nwv3654.css",
85
85
  "/_mochi/import-css/index-xndx4980.css": "import-css/index-xndx4980.css",
@@ -92,8 +92,8 @@
92
92
  "/_mochi/client/chunk-nmf4dq86.js": "svelte-client/chunk-nmf4dq86.js",
93
93
  "/_mochi/client/_hydrate-AppShell_2rtnxn6pgp2rh-f5rzyjm8.js": "svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-f5rzyjm8.js",
94
94
  "/_mochi/client/_hydrate-InstanceView_2ma45l9bybfhl-nv31ptdp.js": "svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-nv31ptdp.js",
95
- "/_mochi/client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js": "svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js",
96
- "/_mochi/client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js": "svelte-client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js"
95
+ "/_mochi/client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js": "svelte-client/_hydrate-SettingsView_8hmarwmf8onq-r9vvc7rx.js",
96
+ "/_mochi/client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js": "svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-fxyd5qx4.js"
97
97
  },
98
98
  "components": {
99
99
  "$mochi/templates/DefaultError.svelte": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebay",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Web UI for spinning up isolated devcontainer instances from any local folder, each with a browser-based VS Code (code-server) — built for parallel Claude Code workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,66 @@
1
+ import { getOption } from '../lib/db.server.ts';
2
+ import { checkPresence, execInContainer } from '../lib/exec.server.ts';
3
+ import { mergeClaudeSettingsScript } from './attention-hooks.ts';
4
+ import { readHostClaudeSettings } from './claude-statusline.ts';
5
+ import type { ContainerTarget, Injection } from '../lib/injections.server.ts';
6
+
7
+ /** The app's own model config (manual override / LiteLLM) exports ANTHROPIC_MODEL, which wins over settings.json. */
8
+ function appOwnsModel(): boolean {
9
+ return (
10
+ getOption('manual_model_override_enabled') === '1' ||
11
+ getOption('custom_endpoint_enabled') === '1'
12
+ );
13
+ }
14
+
15
+ /** The `model` default from host ~/.claude/settings.json, or null when unset or app-owned. */
16
+ export async function hostClaudeModel(): Promise<string | null> {
17
+ if (appOwnsModel()) return null;
18
+ const settings = await readHostClaudeSettings();
19
+ const model = settings?.model;
20
+ return typeof model === 'string' && model.trim() ? model.trim() : null;
21
+ }
22
+
23
+ async function injectModel(
24
+ target: ContainerTarget,
25
+ model: string
26
+ ): Promise<{ ok: boolean; error?: string }> {
27
+ const res = await execInContainer(target, {
28
+ script: mergeClaudeSettingsScript(),
29
+ stdin: JSON.stringify({ model })
30
+ });
31
+ return res.ok ? { ok: true } : { ok: false, error: res.error };
32
+ }
33
+
34
+ /** Mirrors the host's default `model` into the container so subscription instances match the host. */
35
+ export const claudeModel: Injection = {
36
+ id: 'claude-model',
37
+ label: 'model default',
38
+
39
+ auth: {
40
+ hint: 'set `model` in ~/.claude/settings.json',
41
+ async status() {
42
+ const model = await hostClaudeModel();
43
+ return { available: model !== null, source: model ? '~/.claude/settings.json' : null };
44
+ }
45
+ },
46
+
47
+ async apply(target, log) {
48
+ const model = await hostClaudeModel();
49
+ if (!model) return;
50
+ log(`Injecting Claude model default (${model})…\n`);
51
+ const result = await injectModel(target, model);
52
+ log(
53
+ result.ok
54
+ ? '✓ Claude model default configured in container\n'
55
+ : `⚠ Claude model default injection failed: ${result.error}\n`
56
+ );
57
+ },
58
+
59
+ async check(target) {
60
+ return checkPresence(
61
+ target,
62
+ 'h=$(eval echo ~$(id -un)); d="${CLAUDE_CONFIG_DIR:-$h/.claude}"; ' +
63
+ '[ -s "$d/settings.json" ] && grep -q \'"model"\' "$d/settings.json" && echo 1 || echo 0'
64
+ );
65
+ }
66
+ };
@@ -2,10 +2,20 @@ import { checkPresence, execInContainer } from '../lib/exec.server.ts';
2
2
  import { mergeClaudeSettingsScript } from './attention-hooks.ts';
3
3
  import type { ContainerTarget, Injection } from '../lib/injections.server.ts';
4
4
 
5
+ /**
6
+ * Both schemas on purpose: newer `claude` reads the nested `attribution` object, older builds
7
+ * the root-level `includeCoAuthoredBy` — setting both suppresses the byline across versions.
8
+ * Empty `commit`/`pr` strings drop the commit trailer and PR footer.
9
+ */
10
+ export const NO_COAUTHOR_SETTINGS = {
11
+ includeCoAuthoredBy: false,
12
+ attribution: { commit: '', pr: '', includeCoAuthoredBy: false }
13
+ };
14
+
5
15
  async function injectNoCoauthor(target: ContainerTarget): Promise<{ ok: boolean; error?: string }> {
6
16
  const res = await execInContainer(target, {
7
17
  script: mergeClaudeSettingsScript(),
8
- stdin: JSON.stringify({ includeCoAuthoredBy: false })
18
+ stdin: JSON.stringify(NO_COAUTHOR_SETTINGS)
9
19
  });
10
20
  return res.ok ? { ok: true } : { ok: false, error: res.error };
11
21
  }
@@ -1,4 +1,4 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, statSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { join } from 'node:path';
@@ -15,7 +15,7 @@ interface StatusLineConfig {
15
15
  script?: string;
16
16
  }
17
17
 
18
- async function readHostClaudeSettings(): Promise<Record<string, unknown> | null> {
18
+ export async function readHostClaudeSettings(): Promise<Record<string, unknown> | null> {
19
19
  const file = join(homedir(), '.claude', 'settings.json');
20
20
  if (!existsSync(file)) return null;
21
21
  try {
@@ -25,9 +25,23 @@ async function readHostClaudeSettings(): Promise<Record<string, unknown> | null>
25
25
  }
26
26
  }
27
27
 
28
- /** A heuristic: a statusLine command is either a script path or a package runner with no file. */
28
+ export const expandTilde = (p: string): string =>
29
+ p.startsWith('~/') ? join(homedir(), p.slice(2)) : p;
30
+
31
+ /** Only a real file counts — else jq's `//` operator and a bare `/` resolve to the root dir. */
32
+ const isExistingFile = (p: string): boolean => {
33
+ try {
34
+ return statSync(p).isFile();
35
+ } catch {
36
+ return false;
37
+ }
38
+ };
39
+
40
+ /** A heuristic: a statusLine command is either a script path or an inline snippet with no file. */
29
41
  export function extractScriptPath(command: string): string | null {
30
- const token = command.split(/\s+/).find((t) => t.startsWith('/') && existsSync(t));
42
+ const token = command
43
+ .split(/\s+/)
44
+ .find((t) => (t.startsWith('/') || t.startsWith('~/')) && isExistingFile(expandTilde(t)));
31
45
  return token ?? null;
32
46
  }
33
47
 
@@ -37,16 +51,17 @@ export async function readStatusLineConfig(): Promise<StatusLineConfig | null> {
37
51
  const statusLine = settings?.statusLine as { type?: string; command?: string } | undefined;
38
52
  if (!statusLine || statusLine.type !== 'command' || !statusLine.command) return null;
39
53
 
40
- const hostPath = extractScriptPath(statusLine.command);
41
- if (!hostPath) return { statusLine };
54
+ const token = extractScriptPath(statusLine.command);
55
+ if (!token) return { statusLine };
42
56
 
43
57
  let script: string;
44
58
  try {
45
- script = await readFile(hostPath, 'utf8');
59
+ script = await readFile(expandTilde(token), 'utf8');
46
60
  } catch {
47
- return null;
61
+ // A false-positive match must not disable the feature — inject the command verbatim.
62
+ return { statusLine };
48
63
  }
49
- const command = statusLine.command.replaceAll(hostPath, CONTAINER_SCRIPT_PATH);
64
+ const command = statusLine.command.replaceAll(token, CONTAINER_SCRIPT_PATH);
50
65
  return { statusLine: { ...statusLine, command }, script };
51
66
  }
52
67
 
@@ -10,6 +10,7 @@ import { claudeCodeModels } from '../container-injections/claude-code-models.ts'
10
10
  import { githubCredentials } from '../container-injections/github-credentials.ts';
11
11
  import { attentionHooks } from '../container-injections/attention-hooks.ts';
12
12
  import { claudeStatusline } from '../container-injections/claude-statusline.ts';
13
+ import { claudeModel } from '../container-injections/claude-model.ts';
13
14
  import { claudeSkipPermissions } from '../container-injections/claude-skip-permissions.ts';
14
15
  import { claudeAliases } from '../container-injections/claude-aliases.ts';
15
16
  import { claudeNoCoauthor } from '../container-injections/claude-no-coauthor.ts';
@@ -54,6 +55,7 @@ const BASE_INJECTIONS_TAIL: Injection[] = [
54
55
  githubCredentials,
55
56
  attentionHooks,
56
57
  claudeStatusline,
58
+ claudeModel,
57
59
  claudeSkipPermissions,
58
60
  claudeAliases,
59
61
  claudeNoCoauthor,