sideby 0.2.1 → 0.3.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.
- package/README.zh-CN.md +3 -3
- package/dist/build.json +1 -1
- package/dist/src/core/config.d.ts +1 -0
- package/dist/src/core/config.js +3 -0
- package/dist/src/core/launch.d.ts +7 -1
- package/dist/src/core/launch.js +29 -2
- package/dist/src/desktop/icon.js +2 -2
- package/dist/src/families/codex/index.js +2 -0
- package/dist/src/panel/page-styles.js +14 -13
- package/dist/src/runtime.js +16 -2
- package/dist/src/types.d.ts +8 -0
- package/package.json +1 -1
- package/schemas/config.json +4 -0
- package/skills/sideby/SKILL.md +3 -2
- package/skills/sideby/references/badge.md +31 -0
- package/skills/sideby/references/setup.md +3 -2
package/README.zh-CN.md
CHANGED
|
@@ -54,9 +54,9 @@ agent 会检查 Node 和宿主 CLI,装好 sideby 和 skill,建账号,告
|
|
|
54
54
|
<img alt="终端里的 sideby:列出账号、查看额度、推荐下一个账号、发现并修复漂移" src="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/demo.gif" width="1320">
|
|
55
55
|
|
|
56
56
|
<picture>
|
|
57
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel-dark.png">
|
|
58
|
-
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel.png">
|
|
59
|
-
<img alt="sideby 面板:每个账号的 5h、7d 额度,近 7 天 token 用量和健康状态" src="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel.png" width="1280">
|
|
57
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel-dark.zh-CN.png">
|
|
58
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel.zh-CN.png">
|
|
59
|
+
<img alt="sideby 面板:每个账号的 5h、7d 额度,近 7 天 token 用量和健康状态" src="https://raw.githubusercontent.com/Adonis0123/sideby/main/docs/assets/panel.zh-CN.png" width="1280">
|
|
60
60
|
</picture>
|
|
61
61
|
|
|
62
62
|
## 还能做什么
|
package/dist/build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"20261008T112709-0a27b0"}
|
|
@@ -34,6 +34,7 @@ export declare const ConfigSchema: Type.TObject<{
|
|
|
34
34
|
noSymlink: Type.TOptional<Type.TBoolean>;
|
|
35
35
|
credential: Type.TOptional<Type.TBoolean>;
|
|
36
36
|
}>>>>;
|
|
37
|
+
accountTitle: Type.TOptional<Type.TBoolean>;
|
|
37
38
|
resumeRouting: Type.TOptional<Type.TBoolean>;
|
|
38
39
|
shellInitFile: Type.TOptional<Type.TObject<{
|
|
39
40
|
zsh: Type.TOptional<Type.TString>;
|
package/dist/src/core/config.js
CHANGED
|
@@ -57,6 +57,9 @@ export const ConfigSchema = Type.Object({
|
|
|
57
57
|
description: 'Per-Plugin settings; `enabled: false` turns a Plugin off.',
|
|
58
58
|
})),
|
|
59
59
|
extraSharedItems: Type.Optional(Type.Record(Type.String(), Type.Array(SharedItemSchema))),
|
|
60
|
+
accountTitle: Type.Optional(Type.Boolean({
|
|
61
|
+
description: 'Set the terminal title to `[<short command or account>]` when sideby starts an Account, and keep Hosts that allow it (Codex) from replacing it. Off by default.',
|
|
62
|
+
})),
|
|
60
63
|
resumeRouting: Type.Optional(Type.Boolean({
|
|
61
64
|
description: 'Make `sideby shell-init` also define Host-named functions (`claude`, `codex`, `grok`) that resume a session by id in the Account that holds it. Off by default.',
|
|
62
65
|
})),
|
|
@@ -8,9 +8,11 @@ export interface PreparedLaunch {
|
|
|
8
8
|
env: Env;
|
|
9
9
|
/** Printed before launching when sign-in happens inside the Host. */
|
|
10
10
|
notice?: string;
|
|
11
|
+
/** Terminal title set before the Host starts (config `accountTitle`), for example `[codex002]`. */
|
|
12
|
+
title?: string;
|
|
11
13
|
}
|
|
12
14
|
/** What `runHost` needs: a prepared Account launch, or the Host run unchanged (`sideby resume`). */
|
|
13
|
-
export type HostLaunch = Pick<PreparedLaunch, 'family' | 'bin' | 'args' | 'env'>;
|
|
15
|
+
export type HostLaunch = Pick<PreparedLaunch, 'family' | 'bin' | 'args' | 'env' | 'title'>;
|
|
14
16
|
/** Finds an executable on PATH; returns null when the Host is not installed. */
|
|
15
17
|
export declare function which(bin: string, env: Env): Promise<string | null>;
|
|
16
18
|
/**
|
|
@@ -26,6 +28,10 @@ export declare function prepareLaunch(opts: {
|
|
|
26
28
|
userArgs: readonly string[];
|
|
27
29
|
command: 'run' | 'login';
|
|
28
30
|
bus: HookBus;
|
|
31
|
+
/** What the Host may show as the Account (`SIDEBY_LABEL`): the short command, else the ref. */
|
|
32
|
+
label?: string;
|
|
33
|
+
/** Config `accountTitle`: set the terminal title to `[<label>]` and keep the Host from replacing it. */
|
|
34
|
+
title?: boolean;
|
|
29
35
|
}): Promise<PreparedLaunch>;
|
|
30
36
|
/**
|
|
31
37
|
* Runs the Host with the terminal attached and returns sideby's exit code (spec §3.5):
|
package/dist/src/core/launch.js
CHANGED
|
@@ -34,6 +34,10 @@ export async function prepareLaunch(opts) {
|
|
|
34
34
|
Object.assign(env, await readSecretFile(account.secretFile, opts.baseEnv));
|
|
35
35
|
if (!account.isMain)
|
|
36
36
|
env[family.selectVar] = account.dir;
|
|
37
|
+
// Which Account this is, for a status line, prompt or hook to show (spec §3.5); set for every Launch, so a value
|
|
38
|
+
// inherited from another sideby Launch never leaks through.
|
|
39
|
+
env.SIDEBY_ACCOUNT = account.ref;
|
|
40
|
+
env.SIDEBY_LABEL = opts.label || account.ref;
|
|
37
41
|
let args;
|
|
38
42
|
let notice;
|
|
39
43
|
if (opts.command === 'login') {
|
|
@@ -47,7 +51,15 @@ export async function prepareLaunch(opts) {
|
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
else {
|
|
50
|
-
|
|
54
|
+
const keep = opts.title && family.keepTitle && !opts.userArgs.some((a) => a.includes(family.keepTitle.key))
|
|
55
|
+
? family.keepTitle.args
|
|
56
|
+
: [];
|
|
57
|
+
args = [
|
|
58
|
+
...(family.defaultArgs?.(account, opts.userArgs) ?? []),
|
|
59
|
+
...keep,
|
|
60
|
+
...opts.accountArgs,
|
|
61
|
+
...opts.userArgs,
|
|
62
|
+
];
|
|
51
63
|
}
|
|
52
64
|
await opts.bus.run('launch.before', family.id, {
|
|
53
65
|
account,
|
|
@@ -57,7 +69,19 @@ export async function prepareLaunch(opts) {
|
|
|
57
69
|
config: {},
|
|
58
70
|
command: opts.command,
|
|
59
71
|
});
|
|
60
|
-
|
|
72
|
+
// Control characters would end the escape sequence early; labels are shell-safe names, so this is a guard only.
|
|
73
|
+
const title = opts.title
|
|
74
|
+
? [...`[${env.SIDEBY_LABEL ?? account.ref}]`].filter((ch) => ch >= ' ' && ch !== '\x7f').join('')
|
|
75
|
+
: undefined;
|
|
76
|
+
return {
|
|
77
|
+
account,
|
|
78
|
+
family,
|
|
79
|
+
bin: family.bin,
|
|
80
|
+
args,
|
|
81
|
+
env,
|
|
82
|
+
...(notice ? { notice } : {}),
|
|
83
|
+
...(title ? { title } : {}),
|
|
84
|
+
};
|
|
61
85
|
}
|
|
62
86
|
/**
|
|
63
87
|
* Runs the Host with the terminal attached and returns sideby's exit code (spec §3.5):
|
|
@@ -67,6 +91,9 @@ export async function prepareLaunch(opts) {
|
|
|
67
91
|
* default action so Ctrl-Z suspends sideby together with the Host and `fg` resumes both.
|
|
68
92
|
*/
|
|
69
93
|
export function runHost(p) {
|
|
94
|
+
// OSC 0 sets the terminal's tab and window title; only on a terminal, never into a pipe.
|
|
95
|
+
if (p.title && process.stdout.isTTY)
|
|
96
|
+
process.stdout.write(`\x1b]0;${p.title}\x07`);
|
|
70
97
|
return new Promise((resolve) => {
|
|
71
98
|
const child = spawn(p.bin, p.args, { stdio: 'inherit', env: p.env });
|
|
72
99
|
const ignore = () => { };
|
package/dist/src/desktop/icon.js
CHANGED
|
@@ -6,8 +6,8 @@ import { deflateSync } from 'node:zlib';
|
|
|
6
6
|
export const APP_MARKER = 'sideby-app-install';
|
|
7
7
|
// Geometry in the Panel's 40-unit logo box (page.ts: .logo 40×40, radius 10; halves of radius 10.5 centred at
|
|
8
8
|
// 18.5,18.5 facing left and 21.5,21.5 facing right, so they sit 3 apart and mirror through the centre).
|
|
9
|
-
const TILE = { size: 40, radius: 10, color: [
|
|
10
|
-
const MARK = { color: [
|
|
9
|
+
const TILE = { size: 40, radius: 10, color: [0xe5, 0xf2, 0xfb] };
|
|
10
|
+
const MARK = { color: [0x3f, 0x8f, 0xc4], opacities: [1, 0.55] };
|
|
11
11
|
const HALVES = [
|
|
12
12
|
{ cx: 18.5, cy: 18.5, r: 10.5, side: -1 },
|
|
13
13
|
{ cx: 21.5, cy: 21.5, r: 10.5, side: 1 },
|
|
@@ -47,6 +47,8 @@ export const codexFamily = {
|
|
|
47
47
|
return [];
|
|
48
48
|
return ['-c', `${FILE_STORE_KEY}="file"`];
|
|
49
49
|
},
|
|
50
|
+
// `tui.terminal_title` takes only built-in items; an empty list leaves the title sideby set alone.
|
|
51
|
+
keepTitle: { key: 'tui.terminal_title', args: ['-c', 'tui.terminal_title=[]'] },
|
|
50
52
|
async loginState(account) {
|
|
51
53
|
try {
|
|
52
54
|
return (await stat(join(account.dir, 'auth.json'))).isFile() ? 'logged-in' : 'unknown';
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
import { resolveTheme } from './theme.js';
|
|
4
4
|
// Colors defined from other tokens; repeated in both schemes so they follow that scheme's base colors.
|
|
5
5
|
const DERIVED_COLORS = `
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--logo-bg: var(--text); --logo-fg: var(--bg); --logo-line: transparent;
|
|
6
|
+
--btn-text: var(--text); --btn-hover-text: var(--accent); --check: var(--focus);
|
|
7
|
+
--logo-bg: var(--accent-soft); --logo-fg: var(--focus); --logo-line: var(--border);
|
|
9
8
|
--focus-outline: 2px solid var(--focus); --focus-ring: none;`;
|
|
10
9
|
// Design tokens. A host theme (theme.ts) layers its values over these: light values after LIGHT_TOKENS, dark values after
|
|
11
10
|
// DARK_TOKENS. DARK_TOKENS sets every color token again, so a host's light colors never leak into dark mode, while
|
|
@@ -16,22 +15,24 @@ const LIGHT_TOKENS = `
|
|
|
16
15
|
--title-weight: 750; --heading-weight: 650; --strong-weight: 600; --button-weight: 600; --primary-weight: var(--button-weight);
|
|
17
16
|
--radius: 14px; --ctl-radius: 8px; --chip-radius: 999px; --ctl-h: 34px; --ctl-h-sm: 28px; --field-h: 36px;
|
|
18
17
|
--content-width: 1180px; --gutter: 24px;
|
|
19
|
-
--bg: #
|
|
20
|
-
--text: #15171c; --muted: #
|
|
21
|
-
--primary: #
|
|
22
|
-
--
|
|
18
|
+
--bg: #f2f8fc; --surface: #fcfcfa; --surface-2: #f6fafd; --border: #dce8f0; --border-strong: #c5d7e4;
|
|
19
|
+
--text: #15171c; --muted: #555b63; --faint: #8b93a1; --accent: #27658f; --accent-soft: #e5f2fb;
|
|
20
|
+
--primary: #e1f0fb; --primary-hover: #d2e8f9; --primary-active: #c3e0f6; --on-primary: #1f6396;
|
|
21
|
+
--primary-border: #b9d8ef; --primary-hover-border: #9cc7e8; --btn-hover-border: #8fbde3;
|
|
22
|
+
--hover: #f0f7fc; --active: #e6f1fa; --focus: #3f8fc4;
|
|
23
23
|
--ok: #15803d; --ok-bar: #22c55e; --ok-soft: #e8f7ee; --warn: #b45309; --warn-bar: #f59e0b; --warn-soft: #fdf3e2;
|
|
24
|
-
--fail: #c81e1e; --fail-bar: #ef4444; --fail-soft: #fdecec; --track: #
|
|
24
|
+
--fail: #c81e1e; --fail-bar: #ef4444; --fail-soft: #fdecec; --track: #e4edf4;
|
|
25
25
|
--spark: oklch(from var(--accent) clamp(0.55, l, 0.62) max(c, 0.14) h); --spark-soft: oklch(from var(--accent) 0.83 calc(max(c, 0.14) * 0.6) h);
|
|
26
26
|
--shadow: 0 1px 2px rgba(16,24,40,.04), 0 2px 8px rgba(16,24,40,.05);
|
|
27
27
|
--float-shadow: 0 10px 30px rgba(16,24,40,.14), 0 2px 6px rgba(16,24,40,.06);${DERIVED_COLORS}`;
|
|
28
28
|
const DARK_TOKENS = `
|
|
29
|
-
--bg: #
|
|
30
|
-
--text: #
|
|
31
|
-
--primary: #
|
|
32
|
-
--
|
|
29
|
+
--bg: #0e1621; --surface: #141e2a; --surface-2: #111a25; --border: #233244; --border-strong: #30455b;
|
|
30
|
+
--text: #e6edf3; --muted: #9aa9b8; --faint: #6b7a8a; --accent: #8cc4ee; --accent-soft: #16314a;
|
|
31
|
+
--primary: #173a58; --primary-hover: #1d4769; --primary-active: #22537a; --on-primary: #d3e9fa;
|
|
32
|
+
--primary-border: #2b5b82; --primary-hover-border: #3a6f99; --btn-hover-border: #4a7fa8;
|
|
33
|
+
--hover: #182736; --active: #1c2f42; --focus: #5aa7dc;
|
|
33
34
|
--ok: #4ade80; --ok-bar: #22c55e; --ok-soft: #13291c; --warn: #fbbf24; --warn-bar: #f59e0b; --warn-soft: #2d2410;
|
|
34
|
-
--fail: #f87171; --fail-bar: #ef4444; --fail-soft: #331717; --track: #
|
|
35
|
+
--fail: #f87171; --fail-bar: #ef4444; --fail-soft: #331717; --track: #1f2c3a;
|
|
35
36
|
--spark: oklch(from var(--accent) clamp(0.7, l, 0.78) max(c, 0.14) h); --spark-soft: oklch(from var(--accent) 0.45 calc(max(c, 0.14) * 0.6) h);
|
|
36
37
|
--shadow: 0 1px 2px rgba(0,0,0,.3); --float-shadow: 0 12px 32px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.3);${DERIVED_COLORS}`;
|
|
37
38
|
/**
|
package/dist/src/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The one service layer the CLI and the Panel share. Frozen contract for v0.1 (plan: 审核修订).
|
|
2
|
-
import { discoverAccounts, resolveRef } from './core/accounts.js';
|
|
2
|
+
import { aliasesByAccount, discoverAccounts, resolveRef } from './core/accounts.js';
|
|
3
3
|
import { addConfigAlias, aliasAccount, aliasArgs, aliasProblem, aliasRefs, loadConfig, removeConfigAlias, } from './core/config.js';
|
|
4
4
|
import { createAccount } from './core/create.js';
|
|
5
5
|
import { applyFixes, runDoctor } from './core/doctor.js';
|
|
@@ -60,6 +60,17 @@ export async function createRuntime(opts = {}) {
|
|
|
60
60
|
if (f)
|
|
61
61
|
families.set(id, { ...f, sharedItems: [...f.sharedItems, ...extra] });
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* `SIDEBY_LABEL` for a Launch: the alias it was started through; else the Account's aliases, those without Host
|
|
65
|
+
* arguments first, then by name; else the ref.
|
|
66
|
+
*/
|
|
67
|
+
const launchLabel = (ref, account, all) => {
|
|
68
|
+
if (!ref.includes(':') && ownAlias(ref) !== undefined)
|
|
69
|
+
return ref;
|
|
70
|
+
const names = aliasesByAccount(aliasRefs(config.aliases), all).get(account.ref) ?? [];
|
|
71
|
+
const plain = names.filter((n) => aliasArgs(ownAlias(n)).length === 0);
|
|
72
|
+
return plain[0] ?? names[0] ?? account.ref;
|
|
73
|
+
};
|
|
63
74
|
const accounts = async () => {
|
|
64
75
|
const out = [];
|
|
65
76
|
for (const f of families.values())
|
|
@@ -338,8 +349,11 @@ export async function createRuntime(opts = {}) {
|
|
|
338
349
|
return out;
|
|
339
350
|
},
|
|
340
351
|
async prepareLaunch(ref, userArgs, command) {
|
|
341
|
-
const
|
|
352
|
+
const all = await accounts();
|
|
353
|
+
const account = resolveRef(ref, all, aliasRefs(config.aliases));
|
|
342
354
|
return prepareLaunch({
|
|
355
|
+
label: launchLabel(ref, account, all),
|
|
356
|
+
title: config.accountTitle === true,
|
|
343
357
|
account,
|
|
344
358
|
family: familyOf(account.family),
|
|
345
359
|
baseEnv: env,
|
package/dist/src/types.d.ts
CHANGED
|
@@ -67,6 +67,14 @@ export interface FamilyDef {
|
|
|
67
67
|
};
|
|
68
68
|
/** Arguments sideby adds before the Account's and the user's arguments. */
|
|
69
69
|
defaultArgs?(account: Account, userArgs: readonly string[]): string[];
|
|
70
|
+
/**
|
|
71
|
+
* Host arguments that stop the Host from setting the terminal title for this run, so the title sideby sets with
|
|
72
|
+
* config `accountTitle` (`[<SIDEBY_LABEL>]`) stays. Added after `defaultArgs`, unless a user argument contains `key`.
|
|
73
|
+
*/
|
|
74
|
+
keepTitle?: {
|
|
75
|
+
args: string[];
|
|
76
|
+
key: string;
|
|
77
|
+
};
|
|
70
78
|
loginState?(account: Account): Promise<LoginState>;
|
|
71
79
|
model?(account: Account): Promise<string | undefined>;
|
|
72
80
|
/**
|
package/package.json
CHANGED
package/schemas/config.json
CHANGED
|
@@ -145,6 +145,10 @@
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
},
|
|
148
|
+
"accountTitle": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "Set the terminal title to `[<short command or account>]` when sideby starts an Account, and keep Hosts that allow it (Codex) from replacing it. Off by default."
|
|
151
|
+
},
|
|
148
152
|
"resumeRouting": {
|
|
149
153
|
"type": "boolean",
|
|
150
154
|
"description": "Make `sideby shell-init` also define Host-named functions (`claude`, `codex`, `grok`) that resume a session by id in the Account that holds it. Off by default."
|
package/skills/sideby/SKILL.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sideby
|
|
3
|
-
description: Use when the user runs, or wants to run, several AI coding accounts (Claude Code, Codex, Grok Build, pi) side by side. Covers installing sideby and setting up a new account ("install sideby", "set up another Claude account", "add cc008", "配置一个 cc00x", "再加一个 Codex 账号"), explaining what accounts share and what stays separate ("what do my accounts share?", "这些账号共享了什么"), seeing accounts, quota and usage, moving on when one hits its 5-hour or weekly limit, adding a short command (alias), and repairing drifted account directories (skills, hooks, rules or settings not shared, wrong symlinks, credential file permissions). Drives the `sideby` CLI with `--json`; the agent does every step except browser sign-in, filling in secrets and starting interactive sessions, which it hands to the user as one command.
|
|
3
|
+
description: Use when the user runs, or wants to run, several AI coding accounts (Claude Code, Codex, Grok Build, pi) side by side. Covers installing sideby and setting up a new account ("install sideby", "set up another Claude account", "add cc008", "配置一个 cc00x", "再加一个 Codex 账号"), explaining what accounts share and what stays separate ("what do my accounts share?", "这些账号共享了什么"), showing which account a session is ("show [cc001] in the status line", "加上账号标识"), seeing accounts, quota and usage, moving on when one hits its 5-hour or weekly limit, adding a short command (alias), and repairing drifted account directories (skills, hooks, rules or settings not shared, wrong symlinks, credential file permissions). Drives the `sideby` CLI with `--json`; the agent does every step except browser sign-in, filling in secrets and starting interactive sessions, which it hands to the user as one command.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "0.
|
|
5
|
+
version: "0.3.0"
|
|
6
6
|
requires:
|
|
7
7
|
bins: [sideby, node]
|
|
8
8
|
cliHelp: sideby --help
|
|
@@ -37,6 +37,7 @@ Account refs are `<family>:<name>`; a bare `<name>` works only when it is unique
|
|
|
37
37
|
| The user wants to | Run | Then read |
|
|
38
38
|
|---|---|---|
|
|
39
39
|
| Install sideby, or set up a new account ("add cc008") | `sideby --version`, `sideby list --json`, `sideby new <family> --next --json` | `references/setup.md` |
|
|
40
|
+
| Show which account a session is (`[cc001]` in the status line or tab title) | `sideby list --json` for the labels | `references/badge.md` |
|
|
40
41
|
| Know what accounts share and what stays separate | `sideby families <family> --json` | `references/sharing.md` |
|
|
41
42
|
| See accounts, health, quota and usage | `sideby list --json`, `sideby doctor --json`, `sideby quota --json` | `references/diagnose.md` |
|
|
42
43
|
| Repair drift or permissions | `sideby doctor [ref] --json`, then `--fix` after they agree | `references/diagnose.md` |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Show which account a session is
|
|
2
|
+
|
|
3
|
+
For "show the account in the status line", "加上 [cc001] 这个标识". sideby sets `SIDEBY_ACCOUNT` (`claude:001`) and `SIDEBY_LABEL` (`cc001`, the short command, else the ref) for every account it starts. Configure each Host the user runs; tell them what you change. Always check the family prefix of `SIDEBY_ACCOUNT`, because a Host started inside another inherits both variables.
|
|
4
|
+
|
|
5
|
+
## Claude Code: status line
|
|
6
|
+
|
|
7
|
+
1. Read `statusLine` in `~/.claude/settings.json` (linked, so one edit covers every account). Do not touch the env section or anything else in it.
|
|
8
|
+
2. If `command` is `sideby statusline-tap --orig-b64 <base64>`, the real script is the base64-decoded original: edit that script, never the wrapper.
|
|
9
|
+
3. If there is a script, add before its first output: `case "${SIDEBY_ACCOUNT-}" in claude:*) printf '[%s] ' "$SIDEBY_LABEL" ;; esac`. Keep the rest.
|
|
10
|
+
4. If there is no status line: write `~/.claude/statusline.sh` (badge, then `jq -r '.model.display_name // empty'` from stdin), `chmod +x` it, and set `"statusLine": { "type": "command", "command": "~/.claude/statusline.sh" }`. When quota setup was already on with no original command, run `sideby quota teardown claude` first and `sideby quota setup claude` again after (it exits 10; get the user's yes before `--yes`).
|
|
11
|
+
|
|
12
|
+
## Grok Build: status line
|
|
13
|
+
|
|
14
|
+
1. Write `~/.grok/statusline.sh`, `chmod +x`: badge from `SIDEBY_LABEL` when `SIDEBY_ACCOUNT` is `grok:*`, else the `GROK_HOME` directory name; then `jq -r` of `.workspace.current_dir`, `.model.display_name` and `.context_window.used_percentage` from stdin, because `type = "command"` replaces the built-in row.
|
|
15
|
+
2. In `~/.grok/config.toml`, after the `[ui]` keys, add:
|
|
16
|
+
|
|
17
|
+
```toml
|
|
18
|
+
[ui.status_line]
|
|
19
|
+
type = "command"
|
|
20
|
+
command = "~/.grok/statusline.sh"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Never add a second `[ui]` table; if `[ui.status_line]` exists, change it.
|
|
24
|
+
3. `sideby doctor grok --fix` copies `config.toml` to subscription accounts (it must be a real copy there). API accounts (`kind: "api"`) keep their own `config.toml`: add the same table to each.
|
|
25
|
+
4. `GROK_HOME=<dir> grok inspect` must not report the new keys as unrecognized. It takes effect in a new Grok session.
|
|
26
|
+
|
|
27
|
+
## Codex: terminal tab title
|
|
28
|
+
|
|
29
|
+
Codex's status line and title accept only built-in items, so the badge goes in the tab title. Set `"accountTitle": true` in `~/.config/sideby/config.json`, keeping every other key. sideby then sets the title to `[codex002]` at launch and starts Codex with `-c tui.terminal_title=[]` so Codex keeps it; the title no longer shows Codex's own spinner. Check `sideby --version` first: it must be 0.3.0 or later. An older sideby rejects the unknown key and then every sideby command and short command fails; upgrade with `npm i -g sideby` before adding it.
|
|
30
|
+
|
|
31
|
+
Finish by telling the user to start a new session (`cc001`, `grok001`, `codex002`) to see it.
|
|
@@ -15,8 +15,9 @@ For "install sideby", "set up another Claude account", "add cc008", "配置一
|
|
|
15
15
|
- Subscription account: `sideby login <ref>`. It opens the Host's sign-in in their browser. For pi it starts pi; they type `/login`.
|
|
16
16
|
- API account: they open the `proxy.env` path that `new` printed and fill in the values. Never read or write it, and never ask for the key in chat.
|
|
17
17
|
7. **Short command in new terminals.** If `~/.config/sideby/config.json` has no `shellInitFile`, and `~/.zshrc` (bash: `~/.bashrc`) has no line containing `sideby shell-init`, say so and append `eval "$(sideby shell-init zsh)"` (or `bash`). It takes effect in new terminals. Skip this when the user wanted no short command.
|
|
18
|
-
8. **
|
|
19
|
-
9. **
|
|
18
|
+
8. **Account badge.** Offer to show which account each session is (`[cc008]` in the status line or tab title); if they want it, follow `badge.md`.
|
|
19
|
+
9. **Verify.** `sideby doctor <ref> --json` should report no `fail`; fix only the new account with `sideby doctor <ref> --fix`. After the user signed in, `sideby list --json` shows `login: "logged-in"` for it.
|
|
20
|
+
10. **Optional, Claude Code:** for quota in `sideby quota` and the panel, follow `quota.md` (it needs the user's yes).
|
|
20
21
|
|
|
21
22
|
Finish with how to start it: the short command in a new terminal (`cc008`), or `sideby run <ref>`; and `sideby ui` to see every account's quota.
|
|
22
23
|
|