sideby 0.16.0 → 0.17.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.md +5 -3
- package/dist/build.json +1 -1
- package/dist/src/cli/commands.d.ts +16 -0
- package/dist/src/cli/commands.js +460 -5
- package/dist/src/cli/json-schemas.js +115 -0
- package/dist/src/cli/main.js +93 -3
- package/dist/src/core/ask.d.ts +150 -0
- package/dist/src/core/ask.js +365 -0
- package/dist/src/core/attachments.d.ts +50 -0
- package/dist/src/core/attachments.js +442 -0
- package/dist/src/core/autostart.d.ts +88 -0
- package/dist/src/core/autostart.js +256 -0
- package/dist/src/core/config.d.ts +97 -0
- package/dist/src/core/config.js +278 -10
- package/dist/src/core/launch.d.ts +15 -4
- package/dist/src/core/launch.js +27 -23
- package/dist/src/core/local-build.d.ts +17 -0
- package/dist/src/core/local-build.js +39 -3
- package/dist/src/core/probe.d.ts +78 -0
- package/dist/src/core/probe.js +127 -0
- package/dist/src/core/same-login.d.ts +9 -4
- package/dist/src/core/same-login.js +9 -4
- package/dist/src/core/secret-file.d.ts +26 -0
- package/dist/src/core/secret-file.js +126 -7
- package/dist/src/core/terminal.d.ts +93 -0
- package/dist/src/core/terminal.js +371 -0
- package/dist/src/core/update.d.ts +17 -0
- package/dist/src/core/update.js +63 -1
- package/dist/src/families/claude/index.js +3 -0
- package/dist/src/families/claude/quota-cache.d.ts +24 -7
- package/dist/src/families/claude/quota-cache.js +72 -21
- package/dist/src/families/codex/index.js +7 -0
- package/dist/src/families/codex/login-key.d.ts +8 -0
- package/dist/src/families/codex/login-key.js +33 -0
- package/dist/src/families/grok/index.js +3 -0
- package/dist/src/families/login-keys.d.ts +6 -0
- package/dist/src/families/login-keys.js +6 -0
- package/dist/src/families/pi/index.js +4 -0
- package/dist/src/panel/background.js +3 -2
- package/dist/src/panel/handler.d.ts +29 -2
- package/dist/src/panel/handler.js +396 -9
- package/dist/src/panel/i18n.d.ts +175 -2
- package/dist/src/panel/i18n.js +381 -35
- package/dist/src/panel/page-logic.d.ts +214 -0
- package/dist/src/panel/page-logic.js +394 -0
- package/dist/src/panel/page-script.js +984 -134
- package/dist/src/panel/page-styles.js +69 -19
- package/dist/src/panel/page.d.ts +5 -0
- package/dist/src/panel/page.js +2 -0
- package/dist/src/panel/server.d.ts +16 -2
- package/dist/src/panel/server.js +41 -13
- package/dist/src/plugins/check.d.ts +47 -0
- package/dist/src/plugins/check.js +544 -0
- package/dist/src/plugins/scaffold.d.ts +21 -0
- package/dist/src/plugins/scaffold.js +123 -0
- package/dist/src/quota/statusline-tap.js +1 -1
- package/dist/src/runtime.d.ts +10 -0
- package/dist/src/runtime.js +47 -16
- package/dist/src/types.d.ts +19 -0
- package/package.json +2 -2
- package/schemas/ask.json +180 -0
- package/schemas/autostart.json +165 -0
- package/schemas/config-get.json +132 -0
- package/schemas/config-set.json +107 -0
- package/schemas/config.json +73 -6
- package/schemas/key.json +93 -0
- package/schemas/local.json +4 -0
- package/schemas/plugins-check.json +113 -0
- package/schemas/plugins-new.json +87 -0
- package/schemas/terminal.json +152 -0
- package/schemas/try.json +109 -0
- package/schemas/update.json +4 -0
- package/skills/sideby/SKILL.md +12 -6
- package/skills/sideby/references/accounts.md +1 -1
- package/skills/sideby/references/ai-tasks.md +116 -0
- package/skills/sideby/references/panel.md +14 -0
- package/skills/sideby/references/update.md +1 -1
|
@@ -351,6 +351,9 @@ export const OUTPUT_SCHEMAS = {
|
|
|
351
351
|
blocked: Type.Optional(Type.String({ description: 'Why the switch cannot run, with what to do.' })),
|
|
352
352
|
message: Type.Optional(Str),
|
|
353
353
|
restartedPanel: Type.Optional(Type.Boolean()),
|
|
354
|
+
skillSynced: Type.Optional(Type.Boolean({
|
|
355
|
+
description: 'Back on npm, `sideby update --yes` brought the sideby skill to that version (true) or failed (false).',
|
|
356
|
+
})),
|
|
354
357
|
}, { title: 'sideby local --json' }),
|
|
355
358
|
list: Type.Object({
|
|
356
359
|
schemaVersion: Version,
|
|
@@ -706,6 +709,9 @@ export const OUTPUT_SCHEMAS = {
|
|
|
706
709
|
refreshedApp: Type.Optional(Type.Boolean({
|
|
707
710
|
description: 'The desktop app (`sideby app install`) was installed and was written again by the new version.',
|
|
708
711
|
})),
|
|
712
|
+
refreshedAutostart: Type.Optional(Type.Boolean({
|
|
713
|
+
description: 'A login item (`sideby autostart on`) was installed and now starts the new version (false: that failed). Absent without one.',
|
|
714
|
+
})),
|
|
709
715
|
skill: Type.Optional(Type.String({ description: 'The command that installs the sideby skill by hand.' })),
|
|
710
716
|
skillUpdate: Type.Optional(Type.Object({
|
|
711
717
|
installed: Type.Array(Type.Union([Str, Type.Null()]), {
|
|
@@ -736,6 +742,115 @@ export const OUTPUT_SCHEMAS = {
|
|
|
736
742
|
description: '`usage` for a malformed command line (exit 2), a reason such as `no-quota-source` or `alias-invalid`, or `error`.',
|
|
737
743
|
}),
|
|
738
744
|
}, { title: 'sideby error --json' }),
|
|
745
|
+
ask: Type.Object({
|
|
746
|
+
schemaVersion: Version,
|
|
747
|
+
task: Type.Union([Str, Type.Null()]),
|
|
748
|
+
account: Type.Union([Str, Type.Null()]),
|
|
749
|
+
terminal: Type.Union([Str, Type.Null()]),
|
|
750
|
+
opened: Type.Boolean(),
|
|
751
|
+
command: Type.Union([Str, Type.Null()]),
|
|
752
|
+
removedSecrets: Type.Boolean(),
|
|
753
|
+
reason: Type.Optional(Type.String({
|
|
754
|
+
enum: [
|
|
755
|
+
'no-accounts',
|
|
756
|
+
'none-signed-in',
|
|
757
|
+
'all-full',
|
|
758
|
+
'not-installed',
|
|
759
|
+
'automation-denied',
|
|
760
|
+
'npx-cache',
|
|
761
|
+
'unsafe-path',
|
|
762
|
+
'failed',
|
|
763
|
+
'unsupported-platform',
|
|
764
|
+
],
|
|
765
|
+
description: 'Why nothing opened (only when `opened` is false); `detail` says what to do next.',
|
|
766
|
+
})),
|
|
767
|
+
detail: Type.Optional(Type.String({ description: 'What to do next, for a person.' })),
|
|
768
|
+
prompt: Type.Optional(Str),
|
|
769
|
+
attachments: Type.Array(Type.Object({
|
|
770
|
+
path: Type.String({ description: 'Absolute path of the copy saved for the task.' }),
|
|
771
|
+
kind: Type.String({ enum: ['image', 'video'] }),
|
|
772
|
+
bytes: Type.Integer({ minimum: 0 }),
|
|
773
|
+
}), { description: 'Files given with `--attach`, in order; empty without any.' }),
|
|
774
|
+
}),
|
|
775
|
+
terminal: Type.Object({
|
|
776
|
+
schemaVersion: Version,
|
|
777
|
+
preferred: Type.Union([Str, Type.Null()]),
|
|
778
|
+
guess: Type.Object({ id: Str, source: Type.String({ enum: ['config', 'env', 'default'] }) }),
|
|
779
|
+
installed: Type.Array(Type.Object({ id: Str, appPath: Type.Optional(Str), executable: Type.Optional(Str) })),
|
|
780
|
+
terminals: Type.Array(Type.Object({ id: Str, name: Str, installed: Type.Boolean() })),
|
|
781
|
+
}),
|
|
782
|
+
key: Type.Object({
|
|
783
|
+
schemaVersion: Version,
|
|
784
|
+
account: Str,
|
|
785
|
+
var: Str,
|
|
786
|
+
written: Type.Literal(true),
|
|
787
|
+
path: Str,
|
|
788
|
+
}),
|
|
789
|
+
try: Type.Object({
|
|
790
|
+
schemaVersion: Version,
|
|
791
|
+
account: Str,
|
|
792
|
+
confirmed: Type.Boolean(),
|
|
793
|
+
command: Type.Optional(Type.Array(Str)),
|
|
794
|
+
ok: Type.Optional(Type.Boolean()),
|
|
795
|
+
exitCode: Type.Optional(Type.Union([Type.Integer(), Type.Null()])),
|
|
796
|
+
output: Type.Optional(Str),
|
|
797
|
+
timedOut: Type.Optional(Type.Boolean()),
|
|
798
|
+
}),
|
|
799
|
+
'plugins-new': Type.Object({ schemaVersion: Version, dir: Str, files: Type.Array(Str) }),
|
|
800
|
+
'plugins-check': Type.Object({
|
|
801
|
+
schemaVersion: Version,
|
|
802
|
+
ok: Type.Boolean(),
|
|
803
|
+
plugin: Type.Optional(Str),
|
|
804
|
+
families: Type.Array(Str),
|
|
805
|
+
problems: Type.Array(Type.Object({ code: Str, message: Str, fix: Str })),
|
|
806
|
+
}),
|
|
807
|
+
autostart: Type.Object({
|
|
808
|
+
schemaVersion: Version,
|
|
809
|
+
supported: Type.Boolean({
|
|
810
|
+
description: 'False on a system sideby cannot start at login yet; `reason` says so.',
|
|
811
|
+
}),
|
|
812
|
+
reason: Type.Optional(Str),
|
|
813
|
+
state: Type.Union(['off', 'on', 'stale', 'foreign'].map((k) => Type.Literal(k)), {
|
|
814
|
+
description: '`stale`: the Node or sideby it starts no longer exists; `foreign`: a file at that path that sideby did not write.',
|
|
815
|
+
}),
|
|
816
|
+
path: Str,
|
|
817
|
+
programArguments: Type.Optional(Type.Array(Str)),
|
|
818
|
+
current: Type.Boolean({ description: 'The login item starts this sideby with this Node.' }),
|
|
819
|
+
missing: Type.Optional(Type.Array(Str)),
|
|
820
|
+
action: Type.Optional(Type.Union([Type.Literal('on'), Type.Literal('off')])),
|
|
821
|
+
changed: Type.Optional(Type.Boolean()),
|
|
822
|
+
applied: Type.Optional(Type.Boolean()),
|
|
823
|
+
content: Type.Optional(Type.String({ description: 'The file `on` writes.' })),
|
|
824
|
+
commands: Type.Optional(Type.Array(Type.Array(Str))),
|
|
825
|
+
loaded: Type.Optional(Type.Boolean({ description: 'launchd took the change now.' })),
|
|
826
|
+
note: Type.Optional(Str),
|
|
827
|
+
}),
|
|
828
|
+
'config-get': Type.Object({
|
|
829
|
+
schemaVersion: Version,
|
|
830
|
+
file: Type.Optional(Str),
|
|
831
|
+
keys: Type.Optional(Type.Array(Type.Object({
|
|
832
|
+
key: Type.String({ description: 'Dot path; `<…>` marks a part you fill in.' }),
|
|
833
|
+
meaning: Str,
|
|
834
|
+
takes: Str,
|
|
835
|
+
value: Type.Optional(Type.Unknown()),
|
|
836
|
+
entries: Type.Optional(Type.Array(Type.Object({ key: Str, value: Type.Unknown() }))),
|
|
837
|
+
}))),
|
|
838
|
+
key: Type.Optional(Str),
|
|
839
|
+
set: Type.Optional(Type.Boolean()),
|
|
840
|
+
value: Type.Optional(Type.Unknown()),
|
|
841
|
+
meaning: Type.Optional(Str),
|
|
842
|
+
takes: Type.Optional(Str),
|
|
843
|
+
}),
|
|
844
|
+
'config-set': Type.Object({
|
|
845
|
+
schemaVersion: Version,
|
|
846
|
+
action: Type.Union([Type.Literal('set'), Type.Literal('unset')]),
|
|
847
|
+
key: Str,
|
|
848
|
+
file: Str,
|
|
849
|
+
changed: Type.Boolean(),
|
|
850
|
+
applied: Type.Boolean(),
|
|
851
|
+
before: Type.Optional(Type.Unknown()),
|
|
852
|
+
after: Type.Optional(Type.Unknown()),
|
|
853
|
+
}),
|
|
739
854
|
config: ConfigSchema,
|
|
740
855
|
};
|
|
741
856
|
for (const [name, schema] of Object.entries(OUTPUT_SCHEMAS)) {
|
package/dist/src/cli/main.js
CHANGED
|
@@ -7,6 +7,10 @@ const MIN_NODE = [22, 18];
|
|
|
7
7
|
const HELP = `sideby — run every AI coding account side by side
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
+
sideby ask [<text>] [--print] let your AI handle a sideby task
|
|
11
|
+
sideby terminal [--set <id>|--custom <t>|--unset] choose your terminal
|
|
12
|
+
sideby key <account> [--var <NAME>] enter an API key without echo
|
|
13
|
+
sideby try <account> [--yes] try the Host with a short paid request
|
|
10
14
|
sideby list accounts (same as \`sideby list\`)
|
|
11
15
|
sideby run <account> [-- args] start the host CLI for one account
|
|
12
16
|
sideby new <family> <name>|--next [--api] [--alias <short>]
|
|
@@ -71,14 +75,24 @@ Usage:
|
|
|
71
75
|
sideby shell-init [zsh|bash] --write
|
|
72
76
|
rewrite the config's shellInitFile with them
|
|
73
77
|
sideby plugins list loaded plugins and load errors
|
|
78
|
+
sideby plugins new <id> create a local Family Plugin skeleton
|
|
79
|
+
sideby plugins check <id|path> check a plugin and show fixes
|
|
74
80
|
sideby update [--yes] update sideby to the newest version (shows the command first)
|
|
81
|
+
sideby autostart [on|off] [--yes] start the panel in the background at login (macOS; shows the
|
|
82
|
+
file first)
|
|
83
|
+
sideby config get [<key>] every setting with its value and meaning, or one setting
|
|
84
|
+
sideby config set <key> <value> [--yes]
|
|
85
|
+
change any setting (sideby config set panel.theme dark)
|
|
86
|
+
sideby config unset <key> [--yes] back to the default
|
|
75
87
|
sideby local [on|off] [--yes] for sideby developers: switch the global sideby between the
|
|
76
88
|
last local build (pnpm local) and the npm package
|
|
77
89
|
|
|
78
90
|
Options:
|
|
79
91
|
--json machine-readable output (list, new, next, alias, doctor, quota, tune, model, remote-control, handoff,
|
|
80
|
-
families, plugins, app, desktop, browser, update, local
|
|
81
|
-
|
|
92
|
+
families, plugins, app, desktop, browser, update, local, ask, terminal, key, try,
|
|
93
|
+
autostart, config)
|
|
94
|
+
-h, --help show this help; \`sideby <command> --help\` for new, alias, login, doctor, families, desktop, browser,
|
|
95
|
+
autostart, config
|
|
82
96
|
-v, --version show the version
|
|
83
97
|
|
|
84
98
|
An <account> is <family>:<name> (claude:work) or just <name> when it is unique.
|
|
@@ -95,6 +109,11 @@ Updates: once a day sideby asks the npm registry for its newest version and says
|
|
|
95
109
|
Turn it off with NO_UPDATE_NOTIFIER=1 or config "update": { "check": false }; "auto": true installs by itself.`;
|
|
96
110
|
/** `sideby <command> --help` for the commands agents use to set up and repair accounts (spec §3.16). */
|
|
97
111
|
const COMMAND_HELP = {
|
|
112
|
+
ask: 'sideby ask [<text>] [--task free|fix|quota|account|host|plugin|terminal|handoff] [--account <ref>] [--terminal <id>] [--attach <file>]... [--print] [--json]\nsideby ask --run <id> [--state-dir <dir>]\nOpen a task in your terminal. --attach adds an image (PNG, JPEG, GIF, WebP; up to 20 MB) or a video (MP4, MOV, WebM; up to 200 MB), at most 5. --print prints only the prompt. If opening fails, paste the command into a terminal.',
|
|
113
|
+
terminal: "sideby terminal [--set <id>|--custom '<program> … {cwd} … {cmd}'|--unset] [--json]\nList terminals or save your preference; --custom saves and selects your own launch command.",
|
|
114
|
+
key: 'sideby key <account> [--var <NAME>] [--json]\nEnter an API key without echo. Run in a terminal or use the Panel key box.',
|
|
115
|
+
try: 'sideby try <account> [--yes] [--json]\nSend a short request through the Host. This spends a few tokens. Exit 10 waits for --yes.',
|
|
116
|
+
plugins: 'sideby plugins [--json]\nsideby plugins new <id> [--json]\nsideby plugins check <id|path> [--json]\nPlugins run as you, with your permissions. Only load plugins you trust.',
|
|
98
117
|
new: `sideby new <family> <name> [--api] [--alias <short>] [--json]
|
|
99
118
|
sideby new <family> --next [--api] [--alias <short>] [--json]
|
|
100
119
|
|
|
@@ -179,6 +198,42 @@ Examples:
|
|
|
179
198
|
sideby desktop grok-bot:002 --remove --yes
|
|
180
199
|
Exit: 0 opened, brought to the front or removed; 1 not macOS, no desktop app for that host, API account, app not
|
|
181
200
|
installed, a bad window name, or the app did not open; 10 --remove without --yes; 2 malformed command line.`,
|
|
201
|
+
autostart: `sideby autostart [on|off] [--yes] [--json]
|
|
202
|
+
|
|
203
|
+
Start the panel in the background when you log in, so it already shows fresh quota when you open it. It never
|
|
204
|
+
opens a window or a browser and never starts a host. macOS only for now (a LaunchAgent,
|
|
205
|
+
~/Library/LaunchAgents/dev.sideby.panel.plist, that starts this Node and this sideby with ui --background).
|
|
206
|
+
|
|
207
|
+
Without on or off it says whether it is on, off, out of date (the Node or sideby it starts was moved) or blocked
|
|
208
|
+
by a file that is not sideby's. on and off show the file and the launchctl command first; --yes does it.
|
|
209
|
+
\`sideby update\` and \`pnpm local\` keep an existing login item pointing at the sideby in use.
|
|
210
|
+
|
|
211
|
+
Examples:
|
|
212
|
+
sideby autostart --json
|
|
213
|
+
sideby autostart on --yes
|
|
214
|
+
sideby autostart off --yes
|
|
215
|
+
Exit: 0 shown, done or nothing to change; 10 a change was shown and waits for --yes; 1 refused (not macOS, sideby
|
|
216
|
+
in the npx cache or a temporary folder, a file that is not sideby's) or failed; 2 malformed command line.`,
|
|
217
|
+
config: `sideby config get [<key>] [--json]
|
|
218
|
+
sideby config set <key> <value> [--yes] [--json]
|
|
219
|
+
sideby config unset <key> [--yes] [--json]
|
|
220
|
+
|
|
221
|
+
Read or change any setting in sideby's config file. <key> is a dot path such as handoff.threshold or panel.theme;
|
|
222
|
+
\`sideby config get\` lists every key with its value, what it does and the values it takes. A value is read as
|
|
223
|
+
JSON when it parses (90, true, ["cc002","codex001"]), otherwise as text (dark).
|
|
224
|
+
|
|
225
|
+
set and unset show the change first; --yes saves it. The whole file must still be a valid config, and other keys
|
|
226
|
+
and their order are kept. A plugin's own settings are not read or changed here (they may hold secrets), only
|
|
227
|
+
plugins.<plugin>.enabled. accounts.<family>:<name>.args takes only the model and effort arguments, as
|
|
228
|
+
\`sideby model\` does.
|
|
229
|
+
|
|
230
|
+
Examples:
|
|
231
|
+
sideby config get --json
|
|
232
|
+
sideby config set panel.theme dark --yes
|
|
233
|
+
sideby config set handoff.threshold 90 --yes
|
|
234
|
+
sideby config unset panel.language --yes
|
|
235
|
+
Exit: 0 shown, saved or nothing to change; 10 a change was shown and waits for --yes; 1 not a key, a value the key
|
|
236
|
+
does not take, or the file changed meanwhile; 2 malformed command line.`,
|
|
182
237
|
browser: `sideby browser [--enable|--disable] [--yes] [--json]
|
|
183
238
|
sideby browser open <account> [--profile <folder|name>] [--json]
|
|
184
239
|
|
|
@@ -224,6 +279,12 @@ export function nodeTooOld(version = process.versions.node) {
|
|
|
224
279
|
return maj < MIN_NODE[0] || (maj === MIN_NODE[0] && min < MIN_NODE[1]);
|
|
225
280
|
}
|
|
226
281
|
const COMMANDS = {
|
|
282
|
+
ask: { flags: ['json', 'print'], valued: ['task', 'account', 'terminal', 'run', 'state-dir', 'attach'] },
|
|
283
|
+
terminal: { flags: ['json', 'unset'], valued: ['set', 'custom'] },
|
|
284
|
+
key: { flags: ['json'], valued: ['var'] },
|
|
285
|
+
try: { flags: ['json', 'yes'] },
|
|
286
|
+
autostart: { flags: ['json', 'yes', 'refresh'] },
|
|
287
|
+
config: { flags: ['json', 'yes'] },
|
|
227
288
|
list: { flags: ['json'] },
|
|
228
289
|
pin: { flags: ['json'] },
|
|
229
290
|
unpin: { flags: ['json'] },
|
|
@@ -254,10 +315,24 @@ const COMMANDS = {
|
|
|
254
315
|
local: { flags: ['json', 'yes'] },
|
|
255
316
|
'update-check': { flags: [] },
|
|
256
317
|
};
|
|
318
|
+
/** Every value of a repeatable option (`--attach a --attach=b`), in order. */
|
|
319
|
+
function repeatedValues(args, name) {
|
|
320
|
+
const out = [];
|
|
321
|
+
for (let i = 0; i < args.length; i++) {
|
|
322
|
+
const a = args[i];
|
|
323
|
+
if (a === `--${name}` && args[i + 1] !== undefined)
|
|
324
|
+
out.push(args[++i]);
|
|
325
|
+
else if (a.startsWith(`--${name}=`))
|
|
326
|
+
out.push(a.slice(name.length + 3));
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
257
330
|
/** Commands that never print an Update Notice: their output is read by a shell or an agent, or they are internal. */
|
|
258
331
|
function noticeAllowed(cmd, parsed) {
|
|
259
332
|
if (cmd === 'shell-init' || cmd === 'update' || cmd === 'update-check')
|
|
260
333
|
return false;
|
|
334
|
+
if (cmd === 'ask' && (parsed.flags.has('print') || parsed.flags.has('run')))
|
|
335
|
+
return false;
|
|
261
336
|
if (cmd === 'ui' && parsed.flags.has('serve-detached'))
|
|
262
337
|
return false;
|
|
263
338
|
if (cmd === 'handoff' && parsed.positionals[0] === 'ready')
|
|
@@ -349,7 +424,7 @@ export async function main(argv) {
|
|
|
349
424
|
return runHandoffHook(rest, process.env, process.stdin, process.stdout);
|
|
350
425
|
}
|
|
351
426
|
if (nodeTooOld()) {
|
|
352
|
-
console.error(`sideby needs Node ${MIN_NODE.join('.')} or newer (you have ${process.versions.node}). Upgrade: https://nodejs.org
|
|
427
|
+
console.error(`sideby needs Node ${MIN_NODE.join('.')} or newer (you have ${process.versions.node}). Upgrade: https://nodejs.org, or with nvm: \`nvm install 22 && nvm alias default 22\`, then \`npm i -g sideby\` again (each Node version has its own global packages).`);
|
|
353
428
|
return 1;
|
|
354
429
|
}
|
|
355
430
|
if (first === '-v' || first === '--version' || first === 'version') {
|
|
@@ -404,6 +479,9 @@ export async function main(argv) {
|
|
|
404
479
|
console.log(COMMAND_HELP[cmd] ?? HELP);
|
|
405
480
|
return 0;
|
|
406
481
|
}
|
|
482
|
+
// parseArgs keeps the last value of an option; `ask --attach` may be given several times.
|
|
483
|
+
if (cmd === 'ask')
|
|
484
|
+
parsed.attach = repeatedValues(own, 'attach');
|
|
407
485
|
const cmds = await import('./commands.js');
|
|
408
486
|
if (wantsJson && noticeAllowed(cmd, parsed)) {
|
|
409
487
|
notice = await jsonNoticeFor();
|
|
@@ -431,6 +509,14 @@ export async function main(argv) {
|
|
|
431
509
|
}
|
|
432
510
|
async function dispatch(cmd, parsed, io, cmds) {
|
|
433
511
|
switch (cmd) {
|
|
512
|
+
case 'ask':
|
|
513
|
+
return cmds.cmdAsk(parsed, io);
|
|
514
|
+
case 'terminal':
|
|
515
|
+
return cmds.cmdTerminal(parsed, io);
|
|
516
|
+
case 'key':
|
|
517
|
+
return cmds.cmdKey(parsed, io);
|
|
518
|
+
case 'try':
|
|
519
|
+
return cmds.cmdTry(parsed, io);
|
|
434
520
|
case 'pin':
|
|
435
521
|
case 'unpin':
|
|
436
522
|
return await cmds.cmdPin(parsed, io, cmd === 'pin');
|
|
@@ -482,6 +568,10 @@ async function dispatch(cmd, parsed, io, cmds) {
|
|
|
482
568
|
return await cmds.cmdUpdateCheck(parsed, io);
|
|
483
569
|
case 'local':
|
|
484
570
|
return await cmds.cmdLocal(parsed, io);
|
|
571
|
+
case 'autostart':
|
|
572
|
+
return await cmds.cmdAutostart(parsed, io);
|
|
573
|
+
case 'config':
|
|
574
|
+
return await cmds.cmdConfig(parsed, io);
|
|
485
575
|
}
|
|
486
576
|
return 2;
|
|
487
577
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { taskPrompt } from '../panel/page-logic.ts';
|
|
2
|
+
import type { Runtime } from '../runtime.ts';
|
|
3
|
+
import type { Account, Env } from '../types.ts';
|
|
4
|
+
import { type Attachment } from './attachments.ts';
|
|
5
|
+
import { type Paths } from './paths.ts';
|
|
6
|
+
import { type ProbeDeps } from './probe.ts';
|
|
7
|
+
import { type TerminalId } from './terminal.ts';
|
|
8
|
+
export declare function pickTaskAccount(rt: Runtime, opts?: {
|
|
9
|
+
family?: string;
|
|
10
|
+
account?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
account: Account;
|
|
13
|
+
} | {
|
|
14
|
+
reason: 'no-accounts' | 'none-signed-in' | 'all-full';
|
|
15
|
+
}>;
|
|
16
|
+
export interface TaskFile {
|
|
17
|
+
kind: 'prompt' | 'login';
|
|
18
|
+
account: string;
|
|
19
|
+
prompt?: string;
|
|
20
|
+
cwd: string;
|
|
21
|
+
/** The Panel's paths when it wrote the task; `ask --run` starts the Host with the same HOME, config and state. */
|
|
22
|
+
home?: string;
|
|
23
|
+
configDir?: string;
|
|
24
|
+
stateDir?: string;
|
|
25
|
+
/** Files attached to the task (spec §3.28 "附件"), saved under `ai/attachments/<task-id>/`; never their content. */
|
|
26
|
+
attachments?: {
|
|
27
|
+
path: string;
|
|
28
|
+
kind: 'image' | 'video';
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare function taskWorkDir(paths?: Paths): Promise<string>;
|
|
32
|
+
export declare const newTaskId: () => string;
|
|
33
|
+
export declare function writeTask(task: TaskFile, paths?: Paths, id?: string): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* The state folder `ask --run --state-dir` names, accepted only when it and its `ask` folder are real directories the
|
|
36
|
+
* user owns that nobody else can write (the `ask` folder private, as writeTask makes it). Anything else is refused.
|
|
37
|
+
*/
|
|
38
|
+
export declare function taskStateDir(dir: string): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* The Host arguments `ask --run` adds for a prompt task: `<imageArg>=<path>` per attached image for a Family that
|
|
41
|
+
* declares `imageArg` (spec §3.28 "附件"), then the prompt, always last. The `=` form keeps one value per option, so a
|
|
42
|
+
* Host option that takes several values cannot swallow the prompt. An image that is gone, or whose path holds a comma
|
|
43
|
+
* (a Host taking lists may split it), is left to the path in the prompt.
|
|
44
|
+
*/
|
|
45
|
+
export declare function taskHostArgs(rt: Runtime, task: TaskFile): Promise<string[]>;
|
|
46
|
+
/** `env` with the HOME and XDG folders the task was written under, so the Runtime sees what the Panel saw. */
|
|
47
|
+
export declare function taskEnv(task: TaskFile, env: Env): Env;
|
|
48
|
+
export declare function readTask(id: string, paths?: Pick<Paths, 'stateDir'>): Promise<TaskFile>;
|
|
49
|
+
export declare function terminals(rt: Runtime): Promise<{
|
|
50
|
+
installed: import("./terminal.ts").InstalledTerminal[];
|
|
51
|
+
preferred: string | null;
|
|
52
|
+
guess: {
|
|
53
|
+
id: TerminalId;
|
|
54
|
+
source: 'config' | 'env' | 'default';
|
|
55
|
+
};
|
|
56
|
+
terminals: {
|
|
57
|
+
id: TerminalId;
|
|
58
|
+
name: string;
|
|
59
|
+
bundleId: string | null;
|
|
60
|
+
app: string | null;
|
|
61
|
+
termPrograms: readonly string[];
|
|
62
|
+
marker?: string;
|
|
63
|
+
binary?: string;
|
|
64
|
+
installed: boolean;
|
|
65
|
+
}[];
|
|
66
|
+
}>;
|
|
67
|
+
export declare function setPreferredTerminal(rt: Runtime, id?: string): Promise<void>;
|
|
68
|
+
/** Saves `terminal.custom` after checking it names a program and both placeholders; selects it too. */
|
|
69
|
+
export declare function setCustomTerminal(rt: Runtime, template: string): Promise<void>;
|
|
70
|
+
export interface AiTaskInput {
|
|
71
|
+
task?: string;
|
|
72
|
+
text?: string;
|
|
73
|
+
context?: Parameters<typeof taskPrompt>[0]['context'];
|
|
74
|
+
lang?: string;
|
|
75
|
+
account?: string;
|
|
76
|
+
family?: string;
|
|
77
|
+
terminal?: string;
|
|
78
|
+
/** With an explicit `account`: save it as `ask.account`, the next default. */
|
|
79
|
+
remember?: boolean;
|
|
80
|
+
/** Panel uploads (`/api/ask/attach` ids) to attach. */
|
|
81
|
+
uploads?: string[];
|
|
82
|
+
/** The user's own files to attach (`sideby ask --attach`). */
|
|
83
|
+
files?: string[];
|
|
84
|
+
}
|
|
85
|
+
/** The attachments a prompt shown before the task exists lists: uploads still staged, the user's files in place. */
|
|
86
|
+
export declare function previewAttachments(rt: Runtime, input: AiTaskInput): Promise<Attachment[]>;
|
|
87
|
+
export declare function promptForTask(rt: Runtime, input: AiTaskInput, attachments?: readonly Attachment[]): {
|
|
88
|
+
prompt: string;
|
|
89
|
+
removedSecrets: boolean;
|
|
90
|
+
};
|
|
91
|
+
/** Machine-readable reasons an AI Task did not open; `detail` says what to do. */
|
|
92
|
+
export type AiTaskReason = 'no-accounts' | 'none-signed-in' | 'all-full' | 'not-installed' | 'automation-denied' | 'npx-cache' | 'unsafe-path' | 'failed' | 'unsupported-platform';
|
|
93
|
+
export declare function startAiTask(rt: Runtime, input: AiTaskInput): Promise<{
|
|
94
|
+
schemaVersion: 1;
|
|
95
|
+
task: null;
|
|
96
|
+
account: null;
|
|
97
|
+
terminal: null;
|
|
98
|
+
opened: boolean;
|
|
99
|
+
command: null;
|
|
100
|
+
prompt: string;
|
|
101
|
+
reason: AiTaskReason;
|
|
102
|
+
detail: string;
|
|
103
|
+
removedSecrets: boolean;
|
|
104
|
+
attachments: Attachment[];
|
|
105
|
+
} | {
|
|
106
|
+
schemaVersion: 1;
|
|
107
|
+
task: string;
|
|
108
|
+
account: string;
|
|
109
|
+
terminal: string;
|
|
110
|
+
opened: boolean;
|
|
111
|
+
command: string;
|
|
112
|
+
reason?: AiTaskReason | undefined;
|
|
113
|
+
detail?: string | undefined;
|
|
114
|
+
prompt?: undefined;
|
|
115
|
+
removedSecrets: boolean;
|
|
116
|
+
attachments: Attachment[];
|
|
117
|
+
}>;
|
|
118
|
+
export declare function openLoginInTerminal(rt: Runtime, ref: string): Promise<{
|
|
119
|
+
schemaVersion: 1;
|
|
120
|
+
task: string;
|
|
121
|
+
account: string;
|
|
122
|
+
terminal: string;
|
|
123
|
+
opened: boolean;
|
|
124
|
+
command: string;
|
|
125
|
+
reason?: AiTaskReason | undefined;
|
|
126
|
+
detail?: string | undefined;
|
|
127
|
+
}>;
|
|
128
|
+
/**
|
|
129
|
+
* What `try` will run for an Account, computed as `sideby run` would (Family defaults, config arguments) but without
|
|
130
|
+
* reading its Secret File or running `launch.before` hooks: nothing runs before the user agrees.
|
|
131
|
+
*/
|
|
132
|
+
export declare function tryPreview(rt: Runtime, ref: string): Promise<{
|
|
133
|
+
account: Account;
|
|
134
|
+
family: import("../types.ts").FamilyDef;
|
|
135
|
+
command: string[];
|
|
136
|
+
}>;
|
|
137
|
+
/**
|
|
138
|
+
* `sideby try` and the Panel (spec §3.28): runs the Family's probe once the way Launch starts the Host, in a temp
|
|
139
|
+
* folder under the task directory that is removed afterwards. The caller has already said it spends a few tokens. The
|
|
140
|
+
* launch is prepared once here, so `launch.before` hooks run once, and the command returned is what actually ran.
|
|
141
|
+
* `spawn` replaces the process start (tests); by default the Host runs with stdin ignored and is killed at the timeout.
|
|
142
|
+
*/
|
|
143
|
+
export declare function tryAccount(rt: Runtime, ref: string, spawn?: ProbeDeps['spawn']): Promise<{
|
|
144
|
+
ok: boolean;
|
|
145
|
+
exitCode: number | null;
|
|
146
|
+
output: string;
|
|
147
|
+
timedOut: boolean;
|
|
148
|
+
account: string;
|
|
149
|
+
command: string[];
|
|
150
|
+
}>;
|