sideby 0.13.1 → 0.15.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 (85) hide show
  1. package/README.md +2 -1
  2. package/dist/build.json +1 -1
  3. package/dist/src/cli/commands.d.ts +4 -2
  4. package/dist/src/cli/commands.js +194 -11
  5. package/dist/src/cli/format.js +7 -3
  6. package/dist/src/cli/json-schemas.js +104 -3
  7. package/dist/src/cli/main.js +27 -7
  8. package/dist/src/core/config.d.ts +28 -0
  9. package/dist/src/core/config.js +54 -1
  10. package/dist/src/core/handoff-enable.js +10 -5
  11. package/dist/src/core/launch.d.ts +5 -0
  12. package/dist/src/core/launch.js +14 -3
  13. package/dist/src/core/quota-fetch.d.ts +22 -0
  14. package/dist/src/core/quota-fetch.js +98 -0
  15. package/dist/src/core/quota-levels.d.ts +3 -2
  16. package/dist/src/core/quota-levels.js +8 -4
  17. package/dist/src/core/quota-store.d.ts +19 -0
  18. package/dist/src/core/quota-store.js +62 -0
  19. package/dist/src/core/skill-check.d.ts +16 -0
  20. package/dist/src/core/skill-check.js +47 -3
  21. package/dist/src/core/update.d.ts +67 -1
  22. package/dist/src/core/update.js +147 -37
  23. package/dist/src/families/grok/handoff-hook.d.ts +6 -0
  24. package/dist/src/families/grok/handoff-hook.js +45 -2
  25. package/dist/src/families/grok/handoff.d.ts +6 -1
  26. package/dist/src/families/grok/handoff.js +30 -13
  27. package/dist/src/families/grok/index.js +5 -0
  28. package/dist/src/families/grok/quota-log.d.ts +30 -0
  29. package/dist/src/families/grok/quota-log.js +140 -0
  30. package/dist/src/families/grok/quota.d.ts +5 -0
  31. package/dist/src/families/grok/quota.js +112 -0
  32. package/dist/src/panel/handler.d.ts +13 -3
  33. package/dist/src/panel/handler.js +81 -8
  34. package/dist/src/panel/i18n.d.ts +138 -8
  35. package/dist/src/panel/i18n.js +301 -41
  36. package/dist/src/panel/page-logic.d.ts +59 -2
  37. package/dist/src/panel/page-logic.js +99 -6
  38. package/dist/src/panel/page-script.js +769 -175
  39. package/dist/src/panel/page-styles.js +83 -15
  40. package/dist/src/panel/page.js +3 -0
  41. package/dist/src/runtime.d.ts +32 -2
  42. package/dist/src/runtime.js +64 -21
  43. package/dist/src/types.d.ts +49 -1
  44. package/package.json +3 -2
  45. package/schemas/alias.json +1 -1
  46. package/schemas/app-install.json +1 -1
  47. package/schemas/app-uninstall.json +1 -1
  48. package/schemas/browser-open.json +1 -1
  49. package/schemas/browser-switch.json +1 -1
  50. package/schemas/browser.json +1 -1
  51. package/schemas/config.json +60 -0
  52. package/schemas/desktop-remove.json +1 -1
  53. package/schemas/desktop.json +1 -1
  54. package/schemas/doctor.json +1 -1
  55. package/schemas/error.json +1 -1
  56. package/schemas/families.json +1 -1
  57. package/schemas/handoff-disable.json +1 -1
  58. package/schemas/handoff-enable.json +1 -1
  59. package/schemas/handoff-ready.json +1 -1
  60. package/schemas/handoff-setup.json +1 -1
  61. package/schemas/handoff-status.json +282 -1
  62. package/schemas/handoff-teardown.json +1 -1
  63. package/schemas/list.json +6 -1
  64. package/schemas/model.json +1 -1
  65. package/schemas/new.json +1 -1
  66. package/schemas/next.json +1 -1
  67. package/schemas/order.json +148 -0
  68. package/schemas/pin.json +126 -0
  69. package/schemas/plugins.json +1 -1
  70. package/schemas/quota-check.json +88 -0
  71. package/schemas/quota-setup.json +1 -1
  72. package/schemas/quota-teardown.json +1 -1
  73. package/schemas/quota.json +15 -2
  74. package/schemas/remote-control.json +1 -1
  75. package/schemas/tune-change.json +1 -1
  76. package/schemas/tune.json +1 -1
  77. package/schemas/unpin.json +126 -0
  78. package/schemas/update-auto.json +84 -0
  79. package/schemas/update.json +79 -1
  80. package/skills/sideby/SKILL.md +7 -3
  81. package/skills/sideby/references/diagnose.md +2 -1
  82. package/skills/sideby/references/handoff.md +9 -1
  83. package/skills/sideby/references/panel.md +28 -0
  84. package/skills/sideby/references/quota.md +11 -1
  85. package/skills/sideby/references/update.md +6 -2
@@ -21,7 +21,6 @@ const en = {
21
21
  'upd.runs': 'Updating runs:',
22
22
  'upd.after': 'A background panel restarts by itself and this page reloads. A sideby ui running in a terminal and sessions already running keep the old version until you restart them.',
23
23
  'upd.cannot': 'sideby cannot update itself here: {why}. Run this yourself:',
24
- 'upd.skill': 'If you use the sideby skill, install it with:',
25
24
  'upd.skillToo': 'The sideby skill agents read is updated with it.',
26
25
  'upd.skillDone': 'The sideby skill was updated too; agents load it in their next session.',
27
26
  'upd.skillManaged': 'sideby leaves the skill alone ({why}). Update it with:',
@@ -52,6 +51,29 @@ const en = {
52
51
  'upd.done': 'Updated to sideby {v}.',
53
52
  'upd.reloading': 'Waiting for the new version; this page reloads by itself.',
54
53
  'upd.restartHint': 'This page still runs the old version. Restart sideby ui (or reload the page in your portal) to use the new one.',
54
+ 'upd.skillLabel': 'Update skill',
55
+ 'upd.skillLater': 'Once the new version is installed, open this again to bring the sideby skill agents read along.',
56
+ 'upd.skillLabelLong': 'The sideby skill agents read is for {s}; this sideby is {cur}. Click to update it.',
57
+ 'upd.skillBehind': 'The sideby skill agents read is still for {s}.',
58
+ 'upd.skillNoVer': 'an older sideby',
59
+ 'upd.skillWhat': 'Updating brings it to {cur}; agents use it from their next session.',
60
+ 'upd.skillNow': 'Update skill',
61
+ 'upd.skillDoneOnly': 'The sideby skill is up to date; agents use it from their next session.',
62
+ 'upd.skillChanged': 'Nothing to update any more: sideby or the skill changed meanwhile. Look again below.',
63
+ 'upd.manual': 'Update by hand',
64
+ 'upd.localAvail': 'You are running a local build, and sideby {v} is out. Run this in the sideby checkout to go back to npm; the skill comes along:',
65
+ 'upd.autoLocal': 'A local build does not update by itself.',
66
+ 'why.local-build': 'this is a local build',
67
+ 'why.npx': 'it runs from the npx cache, which fetches a version per run',
68
+ 'why.volta': 'Volta manages this install',
69
+ 'why.not-writable': '{at} cannot be written by you',
70
+ 'why.project': 'it is a dependency of a project',
71
+ 'why.source': 'it runs from a source checkout',
72
+ 'why.skill-in-git': 'it is in the git repository {at}',
73
+ 'off.local-build': 'Update checks are off for a local build.',
74
+ 'off.env': 'Update checks are off ({why}).',
75
+ 'off.config': 'Update checks are off (update.check is false in the config).',
76
+ 'off.ci': 'Update checks are off in CI.',
55
77
  'acct.title': 'Accounts',
56
78
  'err.noAnswer': 'sideby did not answer. Retry once it is back.',
57
79
  'err.session': 'Could not renew the session. Reload the page.',
@@ -107,8 +129,8 @@ const en = {
107
129
  'stat.api.one': '{n} API',
108
130
  'stat.api.other': '{n} API',
109
131
  'stat.attention': 'Needs attention',
110
- 'stat.attentionOn': 'Show only these accounts',
111
- 'stat.attentionOff': 'Show all accounts',
132
+ 'stat.attentionOn': 'Show only these',
133
+ 'stat.attentionOff': 'Show all',
112
134
  'stat.btnLabel': '{label}: {value} ({sub}). {action}',
113
135
  'stat.allClear': 'All clear',
114
136
  'stat.tokens': 'Tokens · {n} days',
@@ -143,6 +165,45 @@ const en = {
143
165
  'sort.pressure': 'Sort: quota used',
144
166
  'sort.name': 'Sort: name',
145
167
  'sort.recent': 'Sort: last used',
168
+ 'sort.manual': 'Sort: my order',
169
+ 'order.handle': 'Drag to move, or use the arrow keys',
170
+ 'order.moveFamily': 'Move {name}',
171
+ 'order.moveAccount': 'Move {name}',
172
+ 'order.position': '{name}: place {n} of {total}',
173
+ 'order.failed': 'Could not save the order: {msg}. It is back as it was.',
174
+ 'sort.arrange': 'Arrange order…',
175
+ 'set.order': 'Account order and pins',
176
+ 'set.orderMine': 'Yours',
177
+ 'set.orderDefault': 'Default',
178
+ 'order.resetButton': 'Default order',
179
+ 'order.resetHint': 'Forget the order you gave tools and accounts. Pinned accounts stay pinned.',
180
+ 'order.reset': 'Back to the default order. Pinned accounts stay pinned.',
181
+ 'order.bar': 'Drag, or use ↑ ↓, to change the order. Each change is saved at once.',
182
+ 'order.done': 'Done',
183
+ 'order.saved': 'Order saved',
184
+ 'order.undo': 'Undo',
185
+ 'order.undone': 'Order put back',
186
+ 'order.up': 'Move {name} up',
187
+ 'order.down': 'Move {name} down',
188
+ 'order.sameFamily': 'Accounts move only within {title}.',
189
+ 'pin.pin': 'Pin to top',
190
+ 'pin.unpin': 'Unpin',
191
+ 'pin.pinName': 'Pin {name} to the top of {title}',
192
+ 'pin.unpinName': 'Unpin {name}',
193
+ 'pin.full': 'At most {n} pinned per tool',
194
+ 'pin.mark': 'Pinned',
195
+ 'pin.failed': 'Could not save the pin: {msg}. It is back as it was.',
196
+ 'quota.fetchFailed': 'Could not get the quota',
197
+ 'quota.checkOff': 'Quota check off',
198
+ 'quota.checkOffHint': 'Turn it on in Settings to see this quota.',
199
+ 'quota.fetchedFrom': '{host} asked for this quota {ago}',
200
+ 'quota.lastCheckFailed': 'The latest check failed: {why}',
201
+ 'quota.refreshFailed': 'Could not ask for the newest quota: {msg}',
202
+ 'quota.checkOnToast': '{host} quota check is on',
203
+ 'quota.checkOffToast': '{host} quota check is off',
204
+ 'quota.checkFailed': 'Could not change the setting: {msg}',
205
+ 'set.quotaCheck': '{host} quota',
206
+ 'set.quotaCheckHint': "{host} asks for each account's quota, at most every {n} minutes. sideby never reads its login.",
146
207
  'tb.quota': 'Show quota as',
147
208
  'tb.used': 'Used',
148
209
  'tb.left': 'Left',
@@ -177,9 +238,11 @@ const en = {
177
238
  'acct.signedOut': 'Signed out',
178
239
  'acct.signInHint': 'Copy the sign-in command',
179
240
  'acct.signedOutLabel': '{ref} is signed out: copy the sign-in command',
180
- 'acct.next': 'Next',
181
- 'acct.nextHint': 'The account to move on to when this family runs low: most quota left. Click to copy `sideby next {family}`.',
182
- 'acct.nextLabel': '{ref} is next: copy `sideby next {family}`',
241
+ 'acct.next': 'Suggested',
242
+ 'acct.nextWhy': 'Of the {title} accounts, this one has the most quota left, so it is the one to use next. sideby never switches by itself.',
243
+ 'acct.nextCopy': 'Copy the command that starts it',
244
+ 'stat.needWho': '{names}',
245
+ 'stat.needWhoMore': '{names} and {n} more',
183
246
  'acct.sameLogin': 'Same login as {names}',
184
247
  'acct.sameLoginHint': 'Signed in as the same login as {names}. The vendor counts one quota for all of them, so they show the same quota and a handoff never moves between them.',
185
248
  'acct.notInstalled': 'Tool missing',
@@ -201,13 +264,16 @@ const en = {
201
264
  'quota.dataFrom': 'Data from {ago}',
202
265
  'quota.asOf': 'As of {ago}',
203
266
  'quota.sharedFrom': 'Same login as {name}; quota read from {name}, {ago}',
204
- 'quota.readFrom': 'Quota read from {name}, {ago}',
205
- 'login.count': '{n} directories share one quota',
206
267
  'login.label': 'Same login: {names}',
268
+ 'login.dirs': '{n} directories · shared quota',
269
+ 'login.explain': 'These directories are signed in to the same account, so their quota counts as one.',
270
+ 'login.copyRun': 'Copy start command',
271
+ 'login.showAs': 'Show this name',
272
+ 'login.showAsHint': 'Name the row {name} instead',
273
+ 'login.shown': 'Shown name',
274
+ 'login.shownHint': 'The row is named after this directory',
275
+ 'login.section': '{name}',
207
276
  'login.hint': '{names} are signed in as the same login. The vendor counts one quota for all of them, so it shows once here, and a handoff never moves between them.',
208
- 'login.sharedAbove': 'Uses the quota above',
209
- 'login.collapse': 'Hide {names}',
210
- 'login.expand': 'Show {names}',
211
277
  'quota.noWindows': 'No quota windows reported.',
212
278
  'quota.off': 'Quota not on',
213
279
  'quota.turnOnFamily': 'Turn on quota',
@@ -226,8 +292,38 @@ const en = {
226
292
  'quota.missing': 'No {label} window right now; it shows once that window starts.',
227
293
  'quota.waiting': 'Waiting for the first status line… open a {host} session and send a message.',
228
294
  'col.account': 'Account',
229
- 'col.used': '{label} used',
230
- 'col.left': '{label} left',
295
+ 'col.used': '{label} · used',
296
+ 'col.left': '{label} · left',
297
+ 'finding.link.real-file': "{item} is this account's own copy, but it should be shared with the main account. sideby leaves it alone; see the details for how to share it.",
298
+ 'finding.symlink-forbidden': "{item} is shared with another account, but it must be this account's own copy.",
299
+ 'finding.link.other-target': '{item} is shared with somewhere other than the main account. Fine if you meant it.',
300
+ 'finding.link.dangling': '{item} points to something that no longer exists.',
301
+ 'finding.missing': '{item} is missing from this account.',
302
+ 'finding.copy.type-mismatch': "{item} is a different kind of item than the main account's.",
303
+ 'finding.copy.main-empty': "{item} differs, but the main account's copy is empty, so nothing is copied.",
304
+ 'finding.copy.stale': "{item} is an older copy than the main account's.",
305
+ 'finding.unsupported-type': '{item} is not a file, folder or link.',
306
+ 'finding.json-key.symlink': '{item} is shared with another account, which would mix their sign-ins.',
307
+ 'finding.json-key.main-unreadable': "The main account's {item} cannot be read.",
308
+ 'finding.json-key.main-not-object': "The main account's {item} has an unexpected format.",
309
+ 'finding.json-key.not-object': '{item} has an unexpected format; sideby will not change it.',
310
+ 'finding.json-key.would-remove': '{item} has entries the main account does not; matching it would remove them.',
311
+ 'finding.json-key.drift': '{item} no longer matches the main account.',
312
+ 'finding.credential.mode': '{item} can be read by other users of this computer; it should be private.',
313
+ 'finding.path.invalid': '{item} is a path outside the account folder, so sideby skips it.',
314
+ 'finding.local.is-link': "{item} is shared with another account, but it must be this account's own.",
315
+ 'finding.api.needs-own': "{item} must be this API account's own file.",
316
+ 'finding.main.missing': 'There is no main account to share from.',
317
+ 'finding.name.backup-like': 'This folder looks like a backup, not an account.',
318
+ 'finding.secret.not-file': 'proxy.env must be a regular file.',
319
+ 'finding.plugin.error': 'A plugin could not finish its check.',
320
+ 'finding.extra-check.error': 'An extra check could not finish.',
321
+ 'finding.fixHint': 'Click Fix all to fix it.',
322
+ 'finding.raw': 'Details',
323
+ 'win.5h': '5-hour',
324
+ 'win.7d': 'Weekly',
325
+ 'quota.resetsIn': 'resets in {d}',
326
+ 'quota.staleShort': 'from {ago}',
231
327
  'col.tokens': 'Tokens · {n} days',
232
328
  'col.last': 'Last used',
233
329
  'col.health': 'Health',
@@ -279,13 +375,45 @@ const en = {
279
375
  'fix.hintAction': 'use Fix all',
280
376
  'fix.off': 'Fix all is off: {why}',
281
377
  'fix.whyNoCheck': 'Run a check first.',
282
- 'fix.whyManual': 'None of these findings has an automatic fix; follow the hint under each one.',
378
+ 'fix.whyManual': 'None of these findings has an automatic fix; follow the hint under each one, or use Copy for AI to hand them to your AI agent.',
379
+ 'fix.whyButton': 'Use the button beside each finding.',
380
+ 'fix.copyAi': 'Copy for AI',
381
+ 'fix.copyAiTitle': 'Copy a prompt for your AI agent that asks it to fix the findings without an automatic fix',
382
+ 'fix.promptShown': 'the prompt for your AI agent',
383
+ 'fix.promptLead': "sideby's health check found these problems that it cannot fix by itself. Fix each one by following its hint. Ask me before you delete, move or overwrite any of my files. For sideby commands, use the sideby skill, or read https://github.com/Adonis0123/sideby/blob/main/llms.txt.",
384
+ 'fix.promptHint': 'Hint: ',
385
+ 'fix.promptFixFirst': 'First run `sideby doctor --fix`: it fixes the other findings by itself.',
386
+ 'fix.promptCheck': 'When you are done, run `sideby doctor` and tell me what is left.',
283
387
  'fix.whyNothing': 'Nothing to fix.',
284
388
  'fix.result': 'Fixed {ok} of {n}.',
285
389
  'fix.nothing': 'Nothing needed fixing.',
286
390
  'menu.title': 'More actions',
287
391
  'menu.label': 'Actions for {ref}',
288
392
  'menu.copyRun': 'Copy run command',
393
+ 'menu.groupOpen': 'Open',
394
+ 'ai.title': 'Ask your AI to do it',
395
+ 'ai.installed': 'Skill installed',
396
+ 'ai.notInstalled': 'How',
397
+ 'ai.intro': 'Everything on this page can also be done by your AI coding assistant (Claude Code, Codex, Grok Build and others) with the sideby skill. Say what you want in plain words.',
398
+ 'ai.ready': 'The sideby skill is installed on this computer. Open your assistant and ask.',
399
+ 'ai.install': 'Install the skill once, in a terminal:',
400
+ 'ai.examples': 'Things you can say (click to copy):',
401
+ 'ai.copy': 'Copy',
402
+ 'ai.ask.quota': 'Which of my Claude accounts has the most quota left?',
403
+ 'ai.ask.new': 'Add another Claude account with the short command cc009.',
404
+ 'ai.ask.pin': 'Pin claude:work to the top.',
405
+ 'ai.ask.fix': 'Check my accounts and fix what they should share.',
406
+ 'ai.ask.handoff': 'Turn on Auto Handoff.',
407
+ 'ai.ask.rc': 'Turn off Remote Control for every Claude account.',
408
+ 'ai.orderHint': 'or ask your AI',
409
+ 'ai.emptyHint': 'Or let your AI assistant set it up for you.',
410
+ 'ai.how': 'How',
411
+ 'att.quota': 'Almost full',
412
+ 'att.fix': 'Needs a fix',
413
+ 'att.check': 'Take a look',
414
+ 'menu.groupCopy': 'Copy',
415
+ 'menu.groupManage': 'Manage',
416
+ 'row.copyRun': 'Copy the start command: {cmd}',
289
417
  'menu.copyAlias': 'Copy alias',
290
418
  'menu.copyLogin': 'Copy sign-in command',
291
419
  'menu.copyDir': 'Copy folder path',
@@ -387,7 +515,7 @@ const en = {
387
515
  'hp.how3': 'After a {s}-second countdown (Enter cancels), the next account starts in the same terminal and carries on from the note.',
388
516
  'hp.noStarters': 'None of your accounts can start a hand-over yet. Add a Claude, Codex or Grok account first.',
389
517
  'hp.sameFamily': 'Also switch between accounts of the same tool (for example cc001 to cc002)',
390
- 'hp.turnOn': 'Turn on',
518
+ 'hp.turnOn': 'Turn on Auto Handoff',
391
519
  'hp.turnOff': 'Turn off',
392
520
  'hp.willDo': 'To turn it on, sideby will:',
393
521
  'hp.step.config': 'Switch Auto Handoff on in sideby’s settings',
@@ -454,6 +582,7 @@ const en = {
454
582
  'tune.hostDefaultLong': 'Default (Claude Code decides)',
455
583
  'tune.suggestedTag': 'suggested',
456
584
  'tune.useThis': 'Use this',
585
+ 'tune.custom': 'Choose a size yourself',
457
586
  'tune.working': 'One moment…',
458
587
  'tune.confirmTitle': 'This will change:',
459
588
  'tune.change': 'Auto-compact size',
@@ -477,7 +606,8 @@ const en = {
477
606
  'tpage.none': 'No account has been used enough to estimate yet.',
478
607
  'tpage.idle': 'No use in this period: {list}',
479
608
  'nf.title': 'New account',
480
- 'nf.intro': 'It runs next to your existing accounts and shares their skills and settings.',
609
+ 'nf.intro': 'A new local setup for the tool, next to your other accounts, sharing their skills and settings. You sign in to it after it is made.',
610
+ 'nf.more': 'More options',
481
611
  'nf.tool': 'Tool',
482
612
  'nf.accounts.one': '{n} account',
483
613
  'nf.accounts.other': '{n} accounts',
@@ -486,8 +616,8 @@ const en = {
486
616
  'nf.broken': 'Install broken',
487
617
  'nf.name': 'Name',
488
618
  'nf.namePh': 'name',
489
- 'nf.api': 'API key account',
490
- 'nf.apiHint': 'Gets its own proxy.env for a key or a relay. Leave off for a subscription.',
619
+ 'nf.api': 'Pay with an API key instead of a subscription',
620
+ 'nf.apiHint': 'Leave off if you sign in with a subscription.',
491
621
  'nf.dir': 'Folder',
492
622
  'nf.launch': 'Start with',
493
623
  'nf.create': 'Create account',
@@ -591,9 +721,9 @@ const en = {
591
721
  'set.theme.light': 'Light',
592
722
  'set.theme.dark': 'Dark',
593
723
  'set.rcOn': 'Remote Control starts by itself',
594
- 'rc.button': 'Remote Control',
724
+ 'rc.button': 'Remote Control (continue on phone or web)',
595
725
  'rc.state.on': 'On',
596
- 'rc.state.default': 'Default',
726
+ 'rc.state.default': 'Left to Claude Code',
597
727
  'rc.state.off': 'Off',
598
728
  'rc.state.missing': 'No settings file',
599
729
  'rc.state.blocked': 'Cannot read',
@@ -633,7 +763,6 @@ const zh = {
633
763
  'upd.runs': '更新会运行:',
634
764
  'upd.after': '后台面板会自己重启,本页随后自动刷新。终端里运行的 sideby ui 和已经在跑的会话要重启后才用新版本。',
635
765
  'upd.cannot': '这种安装方式下 sideby 不能自己更新:{why}。请自己运行:',
636
- 'upd.skill': '如果要用 sideby skill,用这条命令安装:',
637
766
  'upd.skillToo': 'agent 读的 sideby skill 会一起更新。',
638
767
  'upd.skillDone': 'sideby skill 也已更新,agent 下次开会话时生效。',
639
768
  'upd.skillManaged': 'sideby 不会自动更新这个 skill({why})。请用这条命令更新:',
@@ -664,6 +793,29 @@ const zh = {
664
793
  'upd.done': '已更新到 sideby {v}。',
665
794
  'upd.reloading': '等待新版本启动,本页会自动刷新。',
666
795
  'upd.restartHint': '本页仍在运行旧版本。重启 sideby ui(在 Portal 里就刷新页面)后使用新版本。',
796
+ 'upd.skillLabel': '更新 skill',
797
+ 'upd.skillLater': '装好新版本后,再打开这里把 agent 用的 sideby skill 一起更新。',
798
+ 'upd.skillLabelLong': 'agent 用的 sideby skill 还是 {s},当前 sideby 是 {cur}。点击更新。',
799
+ 'upd.skillBehind': 'agent 用的 sideby skill 还是 {s}。',
800
+ 'upd.skillNoVer': '旧版本',
801
+ 'upd.skillWhat': '更新后变成 {cur},agent 下次开会话时生效。',
802
+ 'upd.skillNow': '更新 skill',
803
+ 'upd.skillDoneOnly': 'sideby skill 已是最新,agent 下次开会话时生效。',
804
+ 'upd.skillChanged': '现在没有要更新的了:sideby 或 skill 刚刚变过。请看下面的最新状态。',
805
+ 'upd.manual': '手动更新',
806
+ 'upd.localAvail': '你在用本地构建,sideby {v} 已发布。在 sideby 仓库运行这条命令切回 npm 版,skill 会一起同步:',
807
+ 'upd.autoLocal': '本地构建不自动更新。',
808
+ 'why.local-build': '这是本地构建',
809
+ 'why.npx': '它从 npx 缓存运行,每次运行都会取一个版本',
810
+ 'why.volta': '这个安装由 Volta 管理',
811
+ 'why.not-writable': '你没有 {at} 的写权限',
812
+ 'why.project': '它是某个项目的依赖',
813
+ 'why.source': '它从源码仓库运行',
814
+ 'why.skill-in-git': '它在 git 仓库 {at} 里',
815
+ 'off.local-build': '本地构建不检查更新。',
816
+ 'off.env': '检查更新已关闭({why})。',
817
+ 'off.config': '检查更新已关闭(配置里 update.check 为 false)。',
818
+ 'off.ci': 'CI 里不检查更新。',
667
819
  'acct.title': '账号',
668
820
  'err.noAnswer': 'sideby 没有响应,恢复后再重试。',
669
821
  'err.session': '无法续期会话,请刷新页面。',
@@ -719,14 +871,14 @@ const zh = {
719
871
  'stat.api.one': '{n} 个 API',
720
872
  'stat.api.other': '{n} 个 API',
721
873
  'stat.attention': '需要处理',
722
- 'stat.attentionOn': '只看这些账号',
723
- 'stat.attentionOff': '显示全部账号',
874
+ 'stat.attentionOn': '只看这些',
875
+ 'stat.attentionOff': '显示全部',
724
876
  'stat.btnLabel': '{label}:{value}({sub})。{action}',
725
877
  'stat.allClear': '一切正常',
726
878
  'stat.tokens': '近 {n} 天 tokens',
727
879
  'stat.sessions.one': '{n} 个会话',
728
880
  'stat.sessions.other': '{n} 个会话',
729
- 'stat.nextReset': '最近一次额度重置',
881
+ 'stat.nextReset': '下一次额度重置',
730
882
  'stat.noQuota': '暂无额度数据',
731
883
  'need.error.one': '{n} 个读取失败',
732
884
  'need.error.other': '{n} 个读取失败',
@@ -755,6 +907,45 @@ const zh = {
755
907
  'sort.pressure': '排序:额度用量',
756
908
  'sort.name': '排序:名称',
757
909
  'sort.recent': '排序:最近使用',
910
+ 'sort.manual': '排序:我的顺序',
911
+ 'order.handle': '拖动换位置,或用方向键',
912
+ 'order.moveFamily': '移动 {name}',
913
+ 'order.moveAccount': '移动 {name}',
914
+ 'order.position': '{name}:第 {n} 个,共 {total} 个',
915
+ 'order.failed': '顺序没有保存:{msg}。已恢复原来的顺序。',
916
+ 'sort.arrange': '调整顺序…',
917
+ 'set.order': '账号顺序与置顶',
918
+ 'set.orderMine': '已调整',
919
+ 'set.orderDefault': '默认',
920
+ 'order.resetButton': '恢复默认顺序',
921
+ 'order.resetHint': '忘掉你排的工具和账号顺序,置顶会保留',
922
+ 'order.reset': '已恢复默认顺序,置顶保留',
923
+ 'order.bar': '拖动或用 ↑ ↓ 调整顺序,改动立即保存',
924
+ 'order.done': '完成',
925
+ 'order.saved': '已保存顺序',
926
+ 'order.undo': '撤销',
927
+ 'order.undone': '已恢复原来的顺序',
928
+ 'order.up': '上移 {name}',
929
+ 'order.down': '下移 {name}',
930
+ 'order.sameFamily': '账号只能在 {title} 里调整。',
931
+ 'pin.pin': '置顶',
932
+ 'pin.unpin': '取消置顶',
933
+ 'pin.pinName': '把 {name} 置顶到 {title} 最前',
934
+ 'pin.unpinName': '取消置顶 {name}',
935
+ 'pin.full': '每组最多置顶 {n} 个',
936
+ 'pin.mark': '已置顶',
937
+ 'pin.failed': '置顶没有保存:{msg}。已恢复原样。',
938
+ 'quota.fetchFailed': '没拿到额度',
939
+ 'quota.checkOff': '额度查询已关',
940
+ 'quota.checkOffHint': '在「设置」里打开,就能看到这份额度。',
941
+ 'quota.fetchedFrom': '{host} {ago}查询的额度',
942
+ 'quota.lastCheckFailed': '最近一次查询失败:{why}',
943
+ 'quota.refreshFailed': '没能查询最新额度:{msg}',
944
+ 'quota.checkOnToast': '已打开 {host} 额度查询',
945
+ 'quota.checkOffToast': '已关闭 {host} 额度查询',
946
+ 'quota.checkFailed': '设置没有改成:{msg}',
947
+ 'set.quotaCheck': '{host} 额度',
948
+ 'set.quotaCheckHint': '{host} 自己查询每个账号的额度,最多每 {n} 分钟一次。sideby 不读它的登录信息。',
758
949
  'tb.quota': '额度显示方式',
759
950
  'tb.used': '已用',
760
951
  'tb.left': '剩余',
@@ -789,9 +980,11 @@ const zh = {
789
980
  'acct.signedOut': '未登录',
790
981
  'acct.signInHint': '复制登录命令',
791
982
  'acct.signedOutLabel': '{ref} 未登录:复制登录命令',
792
- 'acct.next': '下一个',
793
- 'acct.nextHint': '这个家族额度用完时换到这里:剩余额度最多。点击复制 `sideby next {family}`。',
794
- 'acct.nextLabel': '{ref} 是下一个:复制 `sideby next {family}`',
983
+ 'acct.next': '建议使用',
984
+ 'acct.nextWhy': '{title} 的账号里,这个剩余额度最多,接下来用它最合适。sideby 不会自己切换。',
985
+ 'acct.nextCopy': '复制启动它的命令',
986
+ 'stat.needWho': '{names}',
987
+ 'stat.needWhoMore': '{names} 等 {n} 个',
795
988
  'acct.sameLogin': '与 {names} 同一登录',
796
989
  'acct.sameLoginHint': '与 {names} 用同一个身份登录。供应商只算一份额度,所以它们显示同一份额度,交接也不会在它们之间进行。',
797
990
  'acct.notInstalled': '未安装工具',
@@ -813,13 +1006,16 @@ const zh = {
813
1006
  'quota.dataFrom': '数据来自 {ago}',
814
1007
  'quota.asOf': '截至 {ago}',
815
1008
  'quota.sharedFrom': '与 {name} 同一登录;额度读自 {name},{ago}',
816
- 'quota.readFrom': '额度读自 {name},{ago}',
817
- 'login.count': '{n} 个目录共用一份额度',
818
1009
  'login.label': '同一登录:{names}',
1010
+ 'login.dirs': '{n} 个目录 · 共用额度',
1011
+ 'login.explain': '这几个目录登录的是同一个账号,额度一起算。',
1012
+ 'login.copyRun': '复制启动命令',
1013
+ 'login.showAs': '设为显示名',
1014
+ 'login.showAsHint': '这一行改用 {name} 的名字显示',
1015
+ 'login.shown': '显示名',
1016
+ 'login.shownHint': '这一行用这个目录的名字显示',
1017
+ 'login.section': '{name}',
819
1018
  'login.hint': '{names} 用同一个身份登录。供应商只算一份额度,所以这里只显示一次,交接也不会在它们之间进行。',
820
- 'login.sharedAbove': '共用上方的额度',
821
- 'login.collapse': '收起 {names}',
822
- 'login.expand': '展开 {names}',
823
1019
  'quota.noWindows': '没有额度窗口数据。',
824
1020
  'quota.off': '额度未开启',
825
1021
  'quota.turnOnFamily': '开启额度显示',
@@ -838,8 +1034,38 @@ const zh = {
838
1034
  'quota.missing': '暂无 {label} 窗口,窗口开始后显示。',
839
1035
  'quota.waiting': '等待第一次状态栏刷新…打开 {host} 会话并发一条消息。',
840
1036
  'col.account': '账号',
841
- 'col.used': '{label} 已用',
842
- 'col.left': '{label} 剩余',
1037
+ 'col.used': '{label} · 已用',
1038
+ 'col.left': '{label} · 剩余',
1039
+ 'finding.link.real-file': '{item} 是这个账号自己的一份,本应和主账号共用。sideby 不会动它,怎么改成共用见详情。',
1040
+ 'finding.symlink-forbidden': '{item} 现在和别的账号共用,但它必须是这个账号自己的一份。',
1041
+ 'finding.link.other-target': '{item} 共用的不是主账号的那份。如果是你故意这样设的,可以不管。',
1042
+ 'finding.link.dangling': '{item} 指向的东西已经不存在了。',
1043
+ 'finding.missing': '这个账号缺少 {item}。',
1044
+ 'finding.copy.type-mismatch': '{item} 的类型和主账号的不一样。',
1045
+ 'finding.copy.main-empty': '{item} 和主账号不同,但主账号那份是空的,所以没有复制。',
1046
+ 'finding.copy.stale': '{item} 比主账号的旧。',
1047
+ 'finding.unsupported-type': '{item} 不是文件、文件夹或链接。',
1048
+ 'finding.json-key.symlink': '{item} 和别的账号共用,会把两个账号的登录混在一起。',
1049
+ 'finding.json-key.main-unreadable': '读不了主账号的 {item}。',
1050
+ 'finding.json-key.main-not-object': '主账号的 {item} 格式不对。',
1051
+ 'finding.json-key.not-object': '{item} 格式不对,sideby 不会改它。',
1052
+ 'finding.json-key.would-remove': '{item} 里有主账号没有的条目,同步会把它们删掉。',
1053
+ 'finding.json-key.drift': '{item} 和主账号的不一致了。',
1054
+ 'finding.credential.mode': '{item} 这台电脑上的其他用户也能读到,应该只有你能读。',
1055
+ 'finding.path.invalid': '{item} 的路径跑到账号文件夹外面了,sideby 跳过它。',
1056
+ 'finding.local.is-link': '{item} 现在和别的账号共用,但它必须是这个账号自己的。',
1057
+ 'finding.api.needs-own': '{item} 必须是这个 API 账号自己的文件。',
1058
+ 'finding.main.missing': '没有可以共享的主账号。',
1059
+ 'finding.name.backup-like': '这个文件夹看起来是备份,不是账号。',
1060
+ 'finding.secret.not-file': 'proxy.env 必须是普通文件。',
1061
+ 'finding.plugin.error': '有一个插件没能完成检查。',
1062
+ 'finding.extra-check.error': '有一项额外检查没能完成。',
1063
+ 'finding.fixHint': '点「全部修复」就能修好。',
1064
+ 'finding.raw': '详情',
1065
+ 'win.5h': '5 小时额度',
1066
+ 'win.7d': '每周额度',
1067
+ 'quota.resetsIn': '{d}后重置',
1068
+ 'quota.staleShort': '{ago}的数据',
843
1069
  'col.tokens': 'Tokens · 近 {n} 天',
844
1070
  'col.last': '最近使用',
845
1071
  'col.health': '健康',
@@ -891,13 +1117,45 @@ const zh = {
891
1117
  'fix.hintAction': '点「全部修复」',
892
1118
  'fix.off': '“全部修复”不可用:{why}',
893
1119
  'fix.whyNoCheck': '请先运行检查。',
894
- 'fix.whyManual': '这些发现都不能自动修复,请按每条下面的提示处理。',
1120
+ 'fix.whyManual': '这些发现都不能自动修复:按每条下面的提示处理,或点「复制给 AI 修复」交给你的 AI agent。',
1121
+ 'fix.whyButton': '点每条旁边的按钮修复。',
1122
+ 'fix.copyAi': '复制给 AI 修复',
1123
+ 'fix.copyAiTitle': '复制一段提示词,粘贴给你的 AI agent,让它修好不能自动修复的问题',
1124
+ 'fix.promptShown': '给 AI 的提示词',
1125
+ 'fix.promptLead': 'sideby 的健康检查发现了下面这些它不能自动修复的问题。请逐条按提示修好。删除、移动或覆盖我的文件前先问我。sideby 的命令用法看 sideby skill,或读 https://github.com/Adonis0123/sideby/blob/main/llms.txt。',
1126
+ 'fix.promptHint': '提示:',
1127
+ 'fix.promptFixFirst': '先运行 `sideby doctor --fix`,它会自动修好其余的问题。',
1128
+ 'fix.promptCheck': '修完运行 `sideby doctor`,告诉我还剩什么。',
895
1129
  'fix.whyNothing': '没有需要修复的内容。',
896
1130
  'fix.result': '已修复 {ok} / {n} 项。',
897
1131
  'fix.nothing': '没有需要修复的内容。',
898
1132
  'menu.title': '更多操作',
899
1133
  'menu.label': '{ref} 的操作',
900
1134
  'menu.copyRun': '复制启动命令',
1135
+ 'menu.groupOpen': '打开',
1136
+ 'ai.title': '让 AI 帮你操作',
1137
+ 'ai.installed': '已安装 skill',
1138
+ 'ai.notInstalled': '怎么用',
1139
+ 'ai.intro': '这个页面能做的事,你的 AI 编程助手(Claude Code、Codex、Grok Build 等)装上 sideby skill 后也都能做。用大白话说你想做什么就行。',
1140
+ 'ai.ready': '这台电脑已经装好 sideby skill,打开你的编程助手直接说就行。',
1141
+ 'ai.install': '先在终端里装一次 skill:',
1142
+ 'ai.examples': '可以这样说(点一下复制):',
1143
+ 'ai.copy': '复制',
1144
+ 'ai.ask.quota': '我哪个 Claude 账号剩余额度最多?',
1145
+ 'ai.ask.new': '再加一个 Claude 账号,短命令叫 cc009。',
1146
+ 'ai.ask.pin': '把 claude:work 置顶。',
1147
+ 'ai.ask.fix': '检查一下我的账号,把该共享的修好。',
1148
+ 'ai.ask.handoff': '帮我开启自动交接。',
1149
+ 'ai.ask.rc': '把所有 Claude 账号的远程控制关掉。',
1150
+ 'ai.orderHint': '也可以让 AI 帮你排',
1151
+ 'ai.emptyHint': '也可以让你的 AI 编程助手帮你装好、加账号。',
1152
+ 'ai.how': '怎么做',
1153
+ 'att.quota': '快用完',
1154
+ 'att.fix': '需要修复',
1155
+ 'att.check': '看一下',
1156
+ 'menu.groupCopy': '复制',
1157
+ 'menu.groupManage': '管理',
1158
+ 'row.copyRun': '复制启动命令:{cmd}',
901
1159
  'menu.copyAlias': '复制别名',
902
1160
  'menu.copyLogin': '复制登录命令',
903
1161
  'menu.copyDir': '复制目录路径',
@@ -999,7 +1257,7 @@ const zh = {
999
1257
  'hp.how3': '倒计时 {s} 秒后(按回车可取消),下一个账号在同一个终端里按小结接着做。',
1000
1258
  'hp.noStarters': '你的账号里还没有能发起交接的。先添加一个 Claude、Codex 或 Grok 账号。',
1001
1259
  'hp.sameFamily': '同一个工具的账号之间也切换(例如 cc001 到 cc002)',
1002
- 'hp.turnOn': '一键开启',
1260
+ 'hp.turnOn': '开启自动交接',
1003
1261
  'hp.turnOff': '关闭',
1004
1262
  'hp.willDo': '开启时 sideby 会:',
1005
1263
  'hp.step.config': '在 sideby 的设置里打开自动交接',
@@ -1066,6 +1324,7 @@ const zh = {
1066
1324
  'tune.hostDefaultLong': '默认(由 Claude Code 自己决定)',
1067
1325
  'tune.suggestedTag': '建议',
1068
1326
  'tune.useThis': '使用这个',
1327
+ 'tune.custom': '自己选一个大小',
1069
1328
  'tune.working': '稍等…',
1070
1329
  'tune.confirmTitle': '将会修改:',
1071
1330
  'tune.change': '自动压缩大小',
@@ -1089,7 +1348,8 @@ const zh = {
1089
1348
  'tpage.none': '还没有账号用得够多,没法估算。',
1090
1349
  'tpage.idle': '这段时间没有使用:{list}',
1091
1350
  'nf.title': '新建账号',
1092
- 'nf.intro': '新账号和现有账号并排运行,共享它们的 skills 和设置。',
1351
+ 'nf.intro': '为这个工具新建一份本地配置,和其他账号并排运行,共用它们的技能和设置。建好后再登录。',
1352
+ 'nf.more': '更多选项',
1093
1353
  'nf.tool': '工具',
1094
1354
  'nf.accounts.one': '{n} 个账号',
1095
1355
  'nf.accounts.other': '{n} 个账号',
@@ -1098,8 +1358,8 @@ const zh = {
1098
1358
  'nf.broken': '安装已损坏',
1099
1359
  'nf.name': '名称',
1100
1360
  'nf.namePh': '名称',
1101
- 'nf.api': 'API key 账号',
1102
- 'nf.apiHint': '单独生成 proxy.env,用来填 key 或中转地址。订阅账号不要勾选。',
1361
+ 'nf.api': '用 API key 付费(不用订阅登录)',
1362
+ 'nf.apiHint': '用订阅登录的账号不要勾选。',
1103
1363
  'nf.dir': '目录',
1104
1364
  'nf.launch': '启动命令',
1105
1365
  'nf.create': '创建账号',
@@ -1203,9 +1463,9 @@ const zh = {
1203
1463
  'set.theme.light': '浅色',
1204
1464
  'set.theme.dark': '深色',
1205
1465
  'set.rcOn': 'Remote Control 会自动打开',
1206
- 'rc.button': 'Remote Control',
1466
+ 'rc.button': '远程控制(Remote Control)',
1207
1467
  'rc.state.on': '开',
1208
- 'rc.state.default': '默认',
1468
+ 'rc.state.default': '由 Claude Code 决定',
1209
1469
  'rc.state.off': '关',
1210
1470
  'rc.state.missing': '没有设置文件',
1211
1471
  'rc.state.blocked': '读不了',
@@ -20,6 +20,7 @@ export interface LogicAccount {
20
20
  status: string;
21
21
  windows?: {
22
22
  label: string;
23
+ name?: string;
23
24
  windowMinutes: number;
24
25
  usedPercent: number;
25
26
  resetsAt: string;
@@ -88,8 +89,39 @@ export declare function attentionWhy(a: LogicAccount, health: {
88
89
  key: string;
89
90
  vars: Record<string, string | number>;
90
91
  } | null;
91
- /** Sorted copy: `pressure` (fullest Quota first), `name`, or `recent` (last activity first). The Main Account leads ties. */
92
- export declare function sortAccounts<T extends LogicAccount>(list: T[], mode: string, now: number): T[];
92
+ /**
93
+ * `items` in Display Order (spec §3.25): those whose key is in `order` first, in that order; the others after, in the
94
+ * order they came. Names no longer there are simply not met.
95
+ */
96
+ export declare function inDisplayOrder<T>(items: readonly T[], key: (t: T) => string, order?: readonly string[]): T[];
97
+ /**
98
+ * The Display Order after a drag (spec §3.25). `all` is every key of the list in the current Display Order, hidden
99
+ * ones included; `shown` is what the screen showed, in its order, as units (one key, or a Same Login group that moves
100
+ * as one). The unit at `from` moves to `to`; the shown keys then fill the places shown keys held in `all`, so keys a
101
+ * filter hides keep their place among themselves.
102
+ */
103
+ export declare function reorder(all: readonly string[], shown: readonly (readonly string[])[], from: number, to: number): string[];
104
+ /**
105
+ * Sorted copy: `pressure` (fullest Quota first), `name`, `recent` (last activity first), or `manual` (the Family's
106
+ * Display Order `order`, names not in it after). The Main Account leads ties.
107
+ */
108
+ export declare function sortAccounts<T extends LogicAccount>(list: T[], mode: string, now: number, order?: readonly string[]): T[];
109
+ /**
110
+ * Whether an Account is pinned (spec §3.25): its name is in `pinned`, or, for a Same Login, the name of any of its
111
+ * directories, so the login is pinned as a whole.
112
+ */
113
+ export declare function pinnedIn(a: {
114
+ name: string;
115
+ sameLogin?: readonly string[];
116
+ }, pinned: readonly string[]): boolean;
117
+ /**
118
+ * Pinned Accounts first (spec §3.25): the names in `pinned`, in that order, then the rest in the order they came.
119
+ * A Same Login follows any of its pinned directories, so it stays together. Names no longer there are simply not met.
120
+ */
121
+ export declare function withPinned<T extends {
122
+ name: string;
123
+ sameLogin?: readonly string[];
124
+ }>(list: readonly T[], pinned?: readonly string[]): T[];
93
125
  /**
94
126
  * The sorted list with each Same Login together (spec §3.21): an Account, or the shown members of one login, in
95
127
  * order, at the place of its first member. A login with one shown member stays a plain Account.
@@ -141,6 +173,31 @@ export declare function suggestName(names: string[]): string;
141
173
  * that are not fixable, and `--fix --force`, keep the command: Fix all would not do them.
142
174
  */
143
175
  export declare function panelHint(hint: string, fixable: boolean, action: string): string;
176
+ /**
177
+ * Whether the Panel fixes Finding `f` with a button of its own: a sideby skill behind this version while the
178
+ * Update plan says sideby may update it (`skillUpdatable`). Such Findings stay out of the prompt for an AI agent.
179
+ */
180
+ export declare function panelFixes(f: {
181
+ code?: string;
182
+ }, skillUpdatable: boolean): boolean;
183
+ /**
184
+ * The prompt "Copy for AI" puts on the clipboard: `words.lead`, then each Finding that has no automatic fix
185
+ * (numbered, with its account, item, level, message and hint), then `words.fixFirst` when some Findings do have
186
+ * one, then `words.check`. Empty when every Finding has an automatic fix. `words.hint` labels a hint line.
187
+ */
188
+ export declare function fixPrompt(findings: readonly {
189
+ account: string;
190
+ item: string;
191
+ level: string;
192
+ message: string;
193
+ hint?: string;
194
+ fixable?: boolean;
195
+ }[], words: {
196
+ lead: string;
197
+ hint: string;
198
+ fixFirst: string;
199
+ check: string;
200
+ }): string;
144
201
  /**
145
202
  * A short command to offer for a new Account `name` of a Family, from the config aliases of its existing Accounts:
146
203
  * an alias that is a prefix plus its Account's name (`cc001` for `claude:001`) gives the prefix, the most common