@zergai/ztc 0.10.0-beta.1 → 0.10.1-dev.202611052216
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 +28 -0
- package/bin/zmux-darwin-arm64 +0 -0
- package/bin/ztc-audio-darwin-arm64 +0 -0
- package/bin/ztc-capture-darwin-arm64 +0 -0
- package/bin/ztc-computer-darwin-arm64 +0 -0
- package/bin/ztc-llm-darwin-arm64 +0 -0
- package/bin/ztc-tts-darwin-arm64 +0 -0
- package/bin/ztc-tui-darwin-arm64 +0 -0
- package/bin/zterm-darwin-arm64 +0 -0
- package/dist/agent/commands/agent_cli.d.ts +2 -0
- package/dist/agent/commands/agent_cli.js +1 -0
- package/dist/agent/commands/index.js +2 -1
- package/dist/agent/commands/subs.d.ts +14 -0
- package/dist/agent/commands/subs.js +64 -0
- package/dist/cli_ratatui.d.ts +6 -0
- package/dist/cli_ratatui.js +33 -11
- package/dist/config/types.d.ts +8 -0
- package/dist/mcp/workspace_server.d.ts +2 -0
- package/dist/mcp/workspace_server.js +17 -6
- package/dist/panes/agent_cli.d.ts +8 -6
- package/dist/panes/agent_cli.js +20 -9
- package/dist/panes/agent_panes.d.ts +13 -0
- package/dist/panes/agent_panes.js +28 -3
- package/dist/panes/agent_profiles.d.ts +22 -0
- package/dist/panes/agent_profiles.js +49 -0
- package/dist/panes/motsi.d.ts +35 -0
- package/dist/panes/motsi.js +75 -0
- package/dist/zlm-embed.mjs +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1357,6 +1357,34 @@ Codex pane is typed into it as `[Message from ben (pane 2)] …` (a question als
|
|
|
1357
1357
|
says how to answer), and the answer comes back as a message from that pane.
|
|
1358
1358
|
Broadcasts to `all` reach ZTC agents only.
|
|
1359
1359
|
|
|
1360
|
+
#### Sign-in profiles of ZTC's own
|
|
1361
|
+
|
|
1362
|
+
`/claude @work …` and `/codex @work …` open the pane on a ZTC profile named
|
|
1363
|
+
`work`: a config directory of its own under `~/.ztc/agent-profiles/<cli>/`,
|
|
1364
|
+
handed to the CLI through its documented variable (`CLAUDE_CONFIG_DIR`,
|
|
1365
|
+
`CODEX_HOME`). The first time, the CLI asks you to sign in, with its own login;
|
|
1366
|
+
it keeps that sign-in in the profile, so the CLI's sign-in in iTerm or Ghostty
|
|
1367
|
+
doesn't change. Codex panes on a profile keep their sign-in in the profile's
|
|
1368
|
+
files (`cli_auth_credentials_store="file"`). `/subs` lists your profiles, and
|
|
1369
|
+
`/subs use claude|codex [profile]` sets the one new panes start on (no profile:
|
|
1370
|
+
the CLI's own sign-in). ZTC never reads what a CLI stores in a profile and
|
|
1371
|
+
never moves a pane to another profile on its own. A profile starts without your
|
|
1372
|
+
usual CLI settings, MCP servers or plugins, since those live in the CLI's
|
|
1373
|
+
default config directory.
|
|
1374
|
+
|
|
1375
|
+
#### In Zerg Router's Command Central
|
|
1376
|
+
|
|
1377
|
+
With MotSi installed and its remote view on (`motsi remote view on`), ZTC
|
|
1378
|
+
starts the panes it opens — ZTC agents, Claude Code, Codex, shells and custom
|
|
1379
|
+
panes — through `motsi run --title=<pane name> --`. Zerg Router's Command
|
|
1380
|
+
Central then lists each pane under its name and shows it live as a real
|
|
1381
|
+
terminal. MotSi redacts likely secrets on this computer before anything leaves
|
|
1382
|
+
it, and lets a viewer type only into an agent (Claude Code, Codex or ZTC),
|
|
1383
|
+
never into a shell. Nothing leaves the computer while MotSi's remote view is
|
|
1384
|
+
off. To start panes without MotSi, set `panes.remoteView` to `false` in ZTC's
|
|
1385
|
+
config. The pane ZTC itself starts in is not one ZTC opens; to share it, start
|
|
1386
|
+
ZTC as `motsi run -- ztc`.
|
|
1387
|
+
|
|
1360
1388
|
ZTC's own agent loop uses an Anthropic API key for Claude models (`/config key`
|
|
1361
1389
|
or `ANTHROPIC_API_KEY`). Anthropic's [legal and compliance
|
|
1362
1390
|
terms](https://code.claude.com/docs/en/legal-and-compliance) reserve Claude
|
package/bin/zmux-darwin-arm64
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/ztc-llm-darwin-arm64
CHANGED
|
Binary file
|
package/bin/ztc-tts-darwin-arm64
CHANGED
|
Binary file
|
package/bin/ztc-tui-darwin-arm64
CHANGED
|
Binary file
|
package/bin/zterm-darwin-arm64
CHANGED
|
Binary file
|
|
@@ -15,6 +15,8 @@ export interface AgentCliCommandDeps {
|
|
|
15
15
|
findBinary?: (id: AgentCliId) => string | null;
|
|
16
16
|
server?: () => PaneProgram | Error;
|
|
17
17
|
env?: () => NodeJS.ProcessEnv;
|
|
18
|
+
/** `motsi` to start the pane through; default: MotSi's remote view (see panes/motsi.ts). */
|
|
19
|
+
motsi?: () => string | null;
|
|
18
20
|
}
|
|
19
21
|
export declare function createAgentCliCommand(id: AgentCliId, deps?: AgentCliCommandDeps): Command;
|
|
20
22
|
export declare const claudeCommand: Command;
|
|
@@ -48,6 +48,7 @@ export function createAgentCliCommand(id, deps = {}) {
|
|
|
48
48
|
findBinary: deps.findBinary ?? findAgentCli,
|
|
49
49
|
server: (deps.server ?? workspaceMcpProgram)(),
|
|
50
50
|
env: (deps.env ?? (() => process.env))(),
|
|
51
|
+
...(deps.motsi ? { motsi: deps.motsi() } : {}),
|
|
51
52
|
});
|
|
52
53
|
if (result.error) {
|
|
53
54
|
ctx.addMessage({ role: 'system', content: result.error });
|
|
@@ -31,6 +31,7 @@ import { reloadCommand, restartCommand } from './restart.js';
|
|
|
31
31
|
import { initCommand } from './init.js';
|
|
32
32
|
import { forkCommand, sideCommand } from './fork.js';
|
|
33
33
|
import { claudeCommand, codexCommand } from './agent_cli.js';
|
|
34
|
+
import { subsCommand } from './subs.js';
|
|
34
35
|
import { diffCommand } from './diff.js';
|
|
35
36
|
import { rawCommand } from './raw.js';
|
|
36
37
|
import { themeCommand } from './theme.js';
|
|
@@ -83,7 +84,7 @@ import { effortCommand, thinkingCommand, reasoningCommand, fastCommand } from '.
|
|
|
83
84
|
import { runtimePluginRegistry } from '../../plugin/runtime_registry.js';
|
|
84
85
|
const commandList = [];
|
|
85
86
|
const helpCommand = makeHelpCommand(() => commandList);
|
|
86
|
-
commandList.push(helpCommand, authCommand, loginCommand, logoutCommand, accountsCommand, zergCommand, zergsCommand, configCommand, debugCommand, clearCommand, handoffCommand, statusCommand, runtimeCommand, environmentCommand, jobCommand, shareCommand, hooksCommand, emulationCommand, shellCommand, cdCommand, clipboardCommand, modelsCommand, modelCommand, permissionsCommand, resourcesCommand, initCommand, forkCommand, sideCommand, claudeCommand, codexCommand, diffCommand, rawCommand, themeCommand, doctorCommand, intentCommand, profileCommand, skillsCommand, runbooksCommand, keybindingsCommand, keymapCommand, shortcutsCommand, updateCommand, desktopCommand, layoutCommand, setupCommand, connectCommand, keysCommand, restartCommand, reloadCommand, inputModeCommand, retryCommand, dictationStatusCommand, embeddedCommand, benchCommand, voiceCommand, sfxCommand, compactCommand, automodeCommand, sessionsCommand, resumeCommand, rewindCommand, renameCommand, labelCommand, inboxCommand, attentionCommand, contextCommand, evidenceCommand, boardCommand, usageCommand, shannonRunnerCommand, planCommand, executeCommand, modeCommand, modeListCommand, exitModeCommand, validateCommand, previewCommand, saveCommand, dcaigCommand, manifestCommand, swarmCommand, swarmStopCommand, swarmStatusCommand, superviseCommand, superviseStopCommand, superviseStatusCommand, zlickyCommand, zlickyStopCommand, zlickyStatusCommand, zlickyActCommand, zlickyPresenceCommand, zlickyPttCommand, simCommand, simStopCommand, simStatusCommand, simRunCommand, simCheckpointCommand, simResumeCommand, evolveCommand, evolveStopCommand, evolveStatusCommand, evolveBestCommand, optimizeCommand, batchCommand, batchStatusCommand, batchStopCommand, workflowsCommand, mirrorCommand, settingsCommand, mcpCommand, loopCommand, goalCommand, effortCommand, thinkingCommand, reasoningCommand, fastCommand, exitCommand);
|
|
87
|
+
commandList.push(helpCommand, authCommand, loginCommand, logoutCommand, accountsCommand, zergCommand, zergsCommand, configCommand, debugCommand, clearCommand, handoffCommand, statusCommand, runtimeCommand, environmentCommand, jobCommand, shareCommand, hooksCommand, emulationCommand, shellCommand, cdCommand, clipboardCommand, modelsCommand, modelCommand, permissionsCommand, resourcesCommand, initCommand, forkCommand, sideCommand, claudeCommand, codexCommand, subsCommand, diffCommand, rawCommand, themeCommand, doctorCommand, intentCommand, profileCommand, skillsCommand, runbooksCommand, keybindingsCommand, keymapCommand, shortcutsCommand, updateCommand, desktopCommand, layoutCommand, setupCommand, connectCommand, keysCommand, restartCommand, reloadCommand, inputModeCommand, retryCommand, dictationStatusCommand, embeddedCommand, benchCommand, voiceCommand, sfxCommand, compactCommand, automodeCommand, sessionsCommand, resumeCommand, rewindCommand, renameCommand, labelCommand, inboxCommand, attentionCommand, contextCommand, evidenceCommand, boardCommand, usageCommand, shannonRunnerCommand, planCommand, executeCommand, modeCommand, modeListCommand, exitModeCommand, validateCommand, previewCommand, saveCommand, dcaigCommand, manifestCommand, swarmCommand, swarmStopCommand, swarmStatusCommand, superviseCommand, superviseStopCommand, superviseStatusCommand, zlickyCommand, zlickyStopCommand, zlickyStatusCommand, zlickyActCommand, zlickyPresenceCommand, zlickyPttCommand, simCommand, simStopCommand, simStatusCommand, simRunCommand, simCheckpointCommand, simResumeCommand, evolveCommand, evolveStopCommand, evolveStatusCommand, evolveBestCommand, optimizeCommand, batchCommand, batchStatusCommand, batchStopCommand, workflowsCommand, mirrorCommand, settingsCommand, mcpCommand, loopCommand, goalCommand, effortCommand, thinkingCommand, reasoningCommand, fastCommand, exitCommand);
|
|
87
88
|
export const commands = commandList;
|
|
88
89
|
export function refreshPluginCommands() {
|
|
89
90
|
const existing = new Set(commandList.map(command => command.name));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/subs` — ZTC's sign-in profiles for Claude Code and Codex panes, and which
|
|
3
|
+
* one new panes start on.
|
|
4
|
+
*
|
|
5
|
+
* A profile keeps the CLI's own sign-in in a directory of ZTC's (see
|
|
6
|
+
* panes/agent_profiles.ts), so choosing one here never changes the CLI's
|
|
7
|
+
* sign-in in iTerm or Ghostty. Nothing switches on its own: the user picks.
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from './types.js';
|
|
10
|
+
export interface SubsCommandDeps {
|
|
11
|
+
home?: () => string;
|
|
12
|
+
}
|
|
13
|
+
export declare function createSubsCommand(deps?: SubsCommandDeps): Command;
|
|
14
|
+
export declare const subsCommand: Command;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/subs` — ZTC's sign-in profiles for Claude Code and Codex panes, and which
|
|
3
|
+
* one new panes start on.
|
|
4
|
+
*
|
|
5
|
+
* A profile keeps the CLI's own sign-in in a directory of ZTC's (see
|
|
6
|
+
* panes/agent_profiles.ts), so choosing one here never changes the CLI's
|
|
7
|
+
* sign-in in iTerm or Ghostty. Nothing switches on its own: the user picks.
|
|
8
|
+
*/
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { AGENT_CLIS } from '../../panes/agent_cli.js';
|
|
11
|
+
import { ensureAgentProfile, isProfileName, listAgentProfiles } from '../../panes/agent_profiles.js';
|
|
12
|
+
const USAGE = 'Usage: /subs · /subs use claude|codex [profile] (no profile: the CLI\'s own sign-in)';
|
|
13
|
+
export function createSubsCommand(deps = {}) {
|
|
14
|
+
return {
|
|
15
|
+
name: 'subs',
|
|
16
|
+
description: 'Show and choose the sign-in profiles Claude Code and Codex panes start on',
|
|
17
|
+
usage: '[use <claude|codex> [profile]]',
|
|
18
|
+
handler: async (args, ctx) => {
|
|
19
|
+
const home = (deps.home ?? homedir)();
|
|
20
|
+
const panes = ctx.config.get().panes ?? {};
|
|
21
|
+
const chosen = panes.agentProfiles ?? {};
|
|
22
|
+
if (args.length === 0) {
|
|
23
|
+
const lines = ['claude', 'codex'].map(cli => {
|
|
24
|
+
const label = AGENT_CLIS[cli].label;
|
|
25
|
+
const profiles = listAgentProfiles(cli, home);
|
|
26
|
+
const using = chosen[cli] ? `the "${chosen[cli]}" profile` : `${label}'s own sign-in`;
|
|
27
|
+
const known = profiles.length > 0 ? `Profiles: ${profiles.join(', ')}.` : 'No ZTC profiles yet.';
|
|
28
|
+
return `${label}: new panes use ${using}. ${known}`;
|
|
29
|
+
});
|
|
30
|
+
lines.push('Open one on a profile with /claude @<profile> or /codex @<profile>, and sign in there the first time; '
|
|
31
|
+
+ 'the CLI keeps that sign-in in ZTC\'s profile, so iTerm and Ghostty keep theirs. /subs use claude|codex [profile] sets the default.');
|
|
32
|
+
ctx.addMessage({ role: 'system', content: lines.join('\n') });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const cli = args[1];
|
|
36
|
+
if (args[0] !== 'use' || (cli !== 'claude' && cli !== 'codex') || args.length > 3) {
|
|
37
|
+
ctx.addMessage({ role: 'system', content: USAGE });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const label = AGENT_CLIS[cli].label;
|
|
41
|
+
const name = args[2];
|
|
42
|
+
const next = { ...chosen };
|
|
43
|
+
if (name) {
|
|
44
|
+
if (!isProfileName(name)) {
|
|
45
|
+
ctx.addMessage({ role: 'system', content: `"${name}" is not a profile name: use letters, digits, ".", "_" or "-", up to 32.` });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
ensureAgentProfile(cli, name, home);
|
|
49
|
+
next[cli] = name;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
delete next[cli];
|
|
53
|
+
}
|
|
54
|
+
ctx.config.set('panes', { ...panes, agentProfiles: next });
|
|
55
|
+
ctx.addMessage({
|
|
56
|
+
role: 'system',
|
|
57
|
+
content: name
|
|
58
|
+
? `New ${label} panes use the "${name}" profile. Sign in there the first time one opens; ${label}'s sign-in in other terminals doesn't change.`
|
|
59
|
+
: `New ${label} panes use ${label}'s own sign-in.`,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export const subsCommand = createSubsCommand();
|
package/dist/cli_ratatui.d.ts
CHANGED
|
@@ -1300,6 +1300,12 @@ export declare class RatatuiApp {
|
|
|
1300
1300
|
private spawnAgentCliPane;
|
|
1301
1301
|
/** Where the user's own `claude` and `codex` are installed, if they are. */
|
|
1302
1302
|
private installedAgentClis;
|
|
1303
|
+
/**
|
|
1304
|
+
* The `motsi` that panes start through so Zerg Router's Command Central can
|
|
1305
|
+
* show them, or null: MotSi installed, its remote view on, and
|
|
1306
|
+
* `panes.remoteView` not false (see panes/motsi.ts).
|
|
1307
|
+
*/
|
|
1308
|
+
private paneMotsi;
|
|
1303
1309
|
/**
|
|
1304
1310
|
* Create a ZTC agent pane on the user's behalf: no agent turn, no tool call.
|
|
1305
1311
|
*
|
package/dist/cli_ratatui.js
CHANGED
|
@@ -8,6 +8,7 @@ import { waitForPaneCreatedOrError } from './agent/tools/pane.js';
|
|
|
8
8
|
import { findAgentCli } from './panes/agent_cli.js';
|
|
9
9
|
import { agentCliFromRequestId, formatAgentPaneMessage, openAgentPane, typeLineIntoPane } from './panes/agent_panes.js';
|
|
10
10
|
import { agentCliFromChoice, newPaneChoices } from './panes/new_pane_choices.js';
|
|
11
|
+
import { MOTSI_PANE_ENV, remoteViewMotsi, throughMotsi } from './panes/motsi.js';
|
|
11
12
|
import { IntakeNotifications } from './intake/notifications.js';
|
|
12
13
|
import { projectGoal } from './goal_contract.js';
|
|
13
14
|
import { isToolResultMessage } from './utils/tool_result_message.js';
|
|
@@ -13037,6 +13038,7 @@ export class RatatuiApp {
|
|
|
13037
13038
|
...options,
|
|
13038
13039
|
cwd: options.cwd || this.shellCwd || process.cwd(),
|
|
13039
13040
|
findBinary: id => this.installedAgentClis()[id] ?? null,
|
|
13041
|
+
motsi: this.paneMotsi(),
|
|
13040
13042
|
});
|
|
13041
13043
|
if (result.notice)
|
|
13042
13044
|
this.addSystemMessage(result.notice);
|
|
@@ -13046,6 +13048,14 @@ export class RatatuiApp {
|
|
|
13046
13048
|
installedAgentClis() {
|
|
13047
13049
|
return { claude: findAgentCli('claude'), codex: findAgentCli('codex') };
|
|
13048
13050
|
}
|
|
13051
|
+
/**
|
|
13052
|
+
* The `motsi` that panes start through so Zerg Router's Command Central can
|
|
13053
|
+
* show them, or null: MotSi installed, its remote view on, and
|
|
13054
|
+
* `panes.remoteView` not false (see panes/motsi.ts).
|
|
13055
|
+
*/
|
|
13056
|
+
paneMotsi() {
|
|
13057
|
+
return remoteViewMotsi(configStore.get().panes);
|
|
13058
|
+
}
|
|
13049
13059
|
/**
|
|
13050
13060
|
* Create a ZTC agent pane on the user's behalf: no agent turn, no tool call.
|
|
13051
13061
|
*
|
|
@@ -13096,11 +13106,13 @@ export class RatatuiApp {
|
|
|
13096
13106
|
// leaving the user waiting out the timeout on a failed launch.
|
|
13097
13107
|
(event) => event.type === 'error'
|
|
13098
13108
|
|| (event.type === 'pane_created' && event.request_id === requestId), 15000);
|
|
13109
|
+
const motsi = this.paneMotsi();
|
|
13110
|
+
const launched = motsi ? throughMotsi(motsi, title, child) : child;
|
|
13099
13111
|
const sent = this.tui.send({
|
|
13100
13112
|
type: 'create_pane',
|
|
13101
13113
|
request_id: requestId,
|
|
13102
|
-
command:
|
|
13103
|
-
args:
|
|
13114
|
+
command: launched.command,
|
|
13115
|
+
args: launched.args,
|
|
13104
13116
|
cwd: child.cwd,
|
|
13105
13117
|
title,
|
|
13106
13118
|
pane_type: 'ztc',
|
|
@@ -13108,7 +13120,11 @@ export class RatatuiApp {
|
|
|
13108
13120
|
ratio: options?.ratio ?? 0.5,
|
|
13109
13121
|
target: options?.target,
|
|
13110
13122
|
new_window: options?.newWindow,
|
|
13111
|
-
env: {
|
|
13123
|
+
env: {
|
|
13124
|
+
...this.childZtcPaneEnv(),
|
|
13125
|
+
...(captured ? { ZTC_INITIAL_PANE_INPUT: captured.path } : {}),
|
|
13126
|
+
...(motsi ? MOTSI_PANE_ENV : {}),
|
|
13127
|
+
},
|
|
13112
13128
|
});
|
|
13113
13129
|
if (!sent) {
|
|
13114
13130
|
created.catch(() => { });
|
|
@@ -13204,10 +13220,12 @@ export class RatatuiApp {
|
|
|
13204
13220
|
const requestId = `create-shell-${generateSessionId()}`;
|
|
13205
13221
|
const cwd = options?.cwd || this.shellCwd || process.cwd();
|
|
13206
13222
|
const title = options?.title || 'Shell';
|
|
13223
|
+
const motsi = this.paneMotsi();
|
|
13224
|
+
const launched = motsi ? throughMotsi(motsi, title, { command: shellCommand, args: [] }) : { command: shellCommand, args: [] };
|
|
13207
13225
|
this.trackPendingPaneLaunch(requestId, {
|
|
13208
13226
|
kind: 'process',
|
|
13209
|
-
command:
|
|
13210
|
-
args:
|
|
13227
|
+
command: launched.command,
|
|
13228
|
+
args: launched.args,
|
|
13211
13229
|
cwd,
|
|
13212
13230
|
title,
|
|
13213
13231
|
paneType: 'process',
|
|
@@ -13215,11 +13233,12 @@ export class RatatuiApp {
|
|
|
13215
13233
|
return this.tui.send({
|
|
13216
13234
|
type: 'create_pane',
|
|
13217
13235
|
request_id: requestId,
|
|
13218
|
-
command:
|
|
13219
|
-
args:
|
|
13236
|
+
command: launched.command,
|
|
13237
|
+
args: launched.args,
|
|
13220
13238
|
cwd,
|
|
13221
13239
|
title,
|
|
13222
13240
|
pane_type: 'process',
|
|
13241
|
+
...(motsi ? { env: { ...MOTSI_PANE_ENV } } : {}),
|
|
13223
13242
|
...(options?.direction ? { direction: options.direction } : {}),
|
|
13224
13243
|
...(options?.ratio !== undefined ? { ratio: options.ratio } : {}),
|
|
13225
13244
|
...(options?.target !== undefined ? { target: options.target } : {}),
|
|
@@ -13244,10 +13263,12 @@ export class RatatuiApp {
|
|
|
13244
13263
|
const requestId = `create-command-${generateSessionId()}`;
|
|
13245
13264
|
const cwd = options?.cwd || this.shellCwd || process.cwd();
|
|
13246
13265
|
const title = options?.title || fallbackTitle;
|
|
13266
|
+
const motsi = this.paneMotsi();
|
|
13267
|
+
const launched = motsi ? throughMotsi(motsi, title, { command: shellCommand, args: shellArgs }) : { command: shellCommand, args: shellArgs };
|
|
13247
13268
|
this.trackPendingPaneLaunch(requestId, {
|
|
13248
13269
|
kind: 'process',
|
|
13249
|
-
command:
|
|
13250
|
-
args:
|
|
13270
|
+
command: launched.command,
|
|
13271
|
+
args: launched.args,
|
|
13251
13272
|
cwd,
|
|
13252
13273
|
title,
|
|
13253
13274
|
paneType: 'command',
|
|
@@ -13255,11 +13276,12 @@ export class RatatuiApp {
|
|
|
13255
13276
|
return this.tui.send({
|
|
13256
13277
|
type: 'create_pane',
|
|
13257
13278
|
request_id: requestId,
|
|
13258
|
-
command:
|
|
13259
|
-
args:
|
|
13279
|
+
command: launched.command,
|
|
13280
|
+
args: launched.args,
|
|
13260
13281
|
cwd,
|
|
13261
13282
|
title,
|
|
13262
13283
|
pane_type: 'command',
|
|
13284
|
+
...(motsi ? { env: { ...MOTSI_PANE_ENV } } : {}),
|
|
13263
13285
|
})
|
|
13264
13286
|
? null
|
|
13265
13287
|
: 'failed to create command pane';
|
package/dist/config/types.d.ts
CHANGED
|
@@ -279,6 +279,14 @@ export interface CustomPaneOption {
|
|
|
279
279
|
export interface PanesSettings {
|
|
280
280
|
/** Extra chooser rows, in the order they are offered. */
|
|
281
281
|
custom?: CustomPaneOption[];
|
|
282
|
+
/**
|
|
283
|
+
* Start panes through MotSi (`motsi run`) so Zerg Router's Command Central
|
|
284
|
+
* can show them. Unset: whenever MotSi is installed and its remote view is
|
|
285
|
+
* on. False: never.
|
|
286
|
+
*/
|
|
287
|
+
remoteView?: boolean;
|
|
288
|
+
/** The ZTC sign-in profile new Claude Code / Codex panes start on; unset for the CLI's own sign-in. */
|
|
289
|
+
agentProfiles?: Partial<Record<'claude' | 'codex', string>>;
|
|
282
290
|
}
|
|
283
291
|
/**
|
|
284
292
|
* Which rule triggers auto-compaction. `percent` watches the model's context
|
|
@@ -35,6 +35,8 @@ export interface WorkspaceMcpOptions {
|
|
|
35
35
|
env?: NodeJS.ProcessEnv;
|
|
36
36
|
/** Working directory for panes opened without one. */
|
|
37
37
|
cwd?: string;
|
|
38
|
+
/** `motsi` to start panes through (Zerg Router's Command Central); null for none; default: MotSi's remote view. */
|
|
39
|
+
motsi?: string | null;
|
|
38
40
|
}
|
|
39
41
|
/** The MCP server, connected to a workspace through `transport`. */
|
|
40
42
|
export declare function createWorkspaceMcpServer(options: WorkspaceMcpOptions): Server;
|
|
@@ -18,6 +18,8 @@ import { childZtcPaneEnv, closeTerminalPaneTool, listWorkspacePanes, readTermina
|
|
|
18
18
|
import { AGENT_CLIS, findAgentCli, nextPaneTitle, workspaceMcpProgram } from '../panes/agent_cli.js';
|
|
19
19
|
import { TYPED_ENTER_DELAY_MS, formatAgentPaneMessage, openAgentPane } from '../panes/agent_panes.js';
|
|
20
20
|
import { sessionNamePaneTitle } from '../panes/labels.js';
|
|
21
|
+
import { MOTSI_PANE_ENV, remoteViewMotsi, throughMotsi } from '../panes/motsi.js';
|
|
22
|
+
import { configStore } from '../config.js';
|
|
21
23
|
import { buildZtcPaneLaunchCommand, createZtcLaunchSpec } from '../panes/ztc_launch.js';
|
|
22
24
|
import { PaneRelayClient } from '../ui/ratatui/pane_relay.js';
|
|
23
25
|
import { buildUserSubprocessEnvOverrides } from '../utils/subprocess_env.js';
|
|
@@ -117,6 +119,8 @@ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
117
119
|
export function createWorkspaceMcpServer(options) {
|
|
118
120
|
const { transport, ownPaneId } = options;
|
|
119
121
|
const env = options.env ?? process.env;
|
|
122
|
+
// Panes this agent opens show in Zerg Router's Command Central too, when MotSi's remote view is on.
|
|
123
|
+
const paneMotsi = () => (options.motsi !== undefined ? options.motsi : remoteViewMotsi(configStore.get().panes, { env }));
|
|
120
124
|
if (transport) {
|
|
121
125
|
setPaneToolsTuiProcess(transport);
|
|
122
126
|
if (ownPaneId !== null)
|
|
@@ -184,18 +188,21 @@ export function createWorkspaceMcpServer(options) {
|
|
|
184
188
|
const requestId = `ztc-pane-${randomUUID()}`;
|
|
185
189
|
const created = waitForPaneCreatedOrError(tui, { requestId }, 15000);
|
|
186
190
|
const placement = placementOf(args.placement, ownPaneId);
|
|
191
|
+
const motsi = paneMotsi();
|
|
192
|
+
const child = { command: built.command, args: task ? [...built.args, '--', task] : built.args };
|
|
193
|
+
const launched = motsi ? throughMotsi(motsi, title, child) : child;
|
|
187
194
|
const sent = tui.send({
|
|
188
195
|
type: 'create_pane',
|
|
189
196
|
request_id: requestId,
|
|
190
|
-
command:
|
|
191
|
-
args:
|
|
197
|
+
command: launched.command,
|
|
198
|
+
args: launched.args,
|
|
192
199
|
cwd: built.cwd || cwd,
|
|
193
200
|
title,
|
|
194
201
|
pane_type: 'ztc',
|
|
195
202
|
...(placement.direction ? { direction: placement.direction, ratio: 0.5 } : {}),
|
|
196
203
|
...(placement.target !== undefined ? { target: placement.target } : {}),
|
|
197
204
|
...(placement.newWindow ? { new_window: true } : {}),
|
|
198
|
-
env: childZtcPaneEnv(),
|
|
205
|
+
env: { ...childZtcPaneEnv(), ...(motsi ? MOTSI_PANE_ENV : {}) },
|
|
199
206
|
});
|
|
200
207
|
if (!sent) {
|
|
201
208
|
created.catch(() => { });
|
|
@@ -216,18 +223,21 @@ export function createWorkspaceMcpServer(options) {
|
|
|
216
223
|
const requestId = `create-shell-${randomUUID()}`;
|
|
217
224
|
const created = waitForPaneCreatedOrError(tui, { requestId }, 15000);
|
|
218
225
|
const placement = placementOf(args.placement, ownPaneId);
|
|
226
|
+
const motsi = paneMotsi();
|
|
227
|
+
const shell = { command: env.SHELL || '/bin/sh', args: [] };
|
|
228
|
+
const launched = motsi ? throughMotsi(motsi, title, shell) : shell;
|
|
219
229
|
const sent = tui.send({
|
|
220
230
|
type: 'create_pane',
|
|
221
231
|
request_id: requestId,
|
|
222
|
-
command:
|
|
223
|
-
args:
|
|
232
|
+
command: launched.command,
|
|
233
|
+
args: launched.args,
|
|
224
234
|
cwd,
|
|
225
235
|
title,
|
|
226
236
|
pane_type: 'process',
|
|
227
237
|
...(placement.direction ? { direction: placement.direction, ratio: 0.5 } : {}),
|
|
228
238
|
...(placement.target !== undefined ? { target: placement.target } : {}),
|
|
229
239
|
...(placement.newWindow ? { new_window: true } : {}),
|
|
230
|
-
env: buildUserSubprocessEnvOverrides(),
|
|
240
|
+
env: { ...buildUserSubprocessEnvOverrides(), ...(motsi ? MOTSI_PANE_ENV : {}) },
|
|
231
241
|
});
|
|
232
242
|
if (!sent) {
|
|
233
243
|
created.catch(() => { });
|
|
@@ -278,6 +288,7 @@ export function createWorkspaceMcpServer(options) {
|
|
|
278
288
|
findBinary: options.findBinary ?? findAgentCli,
|
|
279
289
|
server: options.server === undefined ? workspaceMcpProgram(process.argv.slice(0, 2)) : options.server,
|
|
280
290
|
env,
|
|
291
|
+
motsi: paneMotsi(),
|
|
281
292
|
});
|
|
282
293
|
if (result.error)
|
|
283
294
|
throw new ToolError(result.error);
|
|
@@ -46,17 +46,19 @@ export declare class CliArgumentsError extends Error {
|
|
|
46
46
|
export declare function splitCliArguments(raw: string): string[];
|
|
47
47
|
/** Words as a shell would accept them back: quoted only where needed. */
|
|
48
48
|
export declare function formatCliArguments(words: readonly string[]): string;
|
|
49
|
-
/** What to start the CLI with: its own flags, an opening prompt, or both. */
|
|
49
|
+
/** What to start the CLI with: its own flags, an opening prompt, or both, on a ZTC profile or not. */
|
|
50
50
|
export interface AgentLaunchRequest {
|
|
51
51
|
words: string[];
|
|
52
52
|
prompt?: string;
|
|
53
|
+
/** A ZTC sign-in profile (see panes/agent_profiles.ts). */
|
|
54
|
+
profile?: string;
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
|
-
* Read the text after `/claude` or `/codex`.
|
|
56
|
-
*
|
|
57
|
-
* --continue`, `/codex -m gpt-5
|
|
58
|
-
* prompt, kept exactly as typed,
|
|
59
|
-
* migrations`).
|
|
57
|
+
* Read the text after `/claude` or `/codex`. A leading `@name` picks a ZTC
|
|
58
|
+
* sign-in profile. Text that starts with a flag is the CLI's own arguments,
|
|
59
|
+
* split like a shell command line (`/claude --continue`, `/codex -m gpt-5
|
|
60
|
+
* "review this"`). Anything else is the opening prompt, kept exactly as typed,
|
|
61
|
+
* apostrophes and all (`/claude don't touch the migrations`).
|
|
60
62
|
*/
|
|
61
63
|
export declare function parseAgentCommandBody(body: string): AgentLaunchRequest;
|
|
62
64
|
export interface PaneProgram {
|
package/dist/panes/agent_cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { accessSync, constants, existsSync, statSync } from 'node:fs';
|
|
|
16
16
|
import { homedir } from 'node:os';
|
|
17
17
|
import { basename, dirname, join } from 'node:path';
|
|
18
18
|
import { resolveOwnedCliLaunch } from '../desktop/cli_launch.js';
|
|
19
|
+
import { isProfileName } from './agent_profiles.js';
|
|
19
20
|
import { buildUserSubprocessEnvOverrides } from '../utils/subprocess_env.js';
|
|
20
21
|
export const AGENT_CLIS = Object.freeze({
|
|
21
22
|
claude: {
|
|
@@ -160,19 +161,29 @@ export function formatCliArguments(words) {
|
|
|
160
161
|
.join(' ');
|
|
161
162
|
}
|
|
162
163
|
/**
|
|
163
|
-
* Read the text after `/claude` or `/codex`.
|
|
164
|
-
*
|
|
165
|
-
* --continue`, `/codex -m gpt-5
|
|
166
|
-
* prompt, kept exactly as typed,
|
|
167
|
-
* migrations`).
|
|
164
|
+
* Read the text after `/claude` or `/codex`. A leading `@name` picks a ZTC
|
|
165
|
+
* sign-in profile. Text that starts with a flag is the CLI's own arguments,
|
|
166
|
+
* split like a shell command line (`/claude --continue`, `/codex -m gpt-5
|
|
167
|
+
* "review this"`). Anything else is the opening prompt, kept exactly as typed,
|
|
168
|
+
* apostrophes and all (`/claude don't touch the migrations`).
|
|
168
169
|
*/
|
|
169
170
|
export function parseAgentCommandBody(body) {
|
|
170
|
-
|
|
171
|
+
let text = body.trim();
|
|
172
|
+
let profile;
|
|
173
|
+
const named = /^@(\S+)(?:\s+|$)/.exec(text);
|
|
174
|
+
if (named) {
|
|
175
|
+
profile = named[1];
|
|
176
|
+
if (!isProfileName(profile)) {
|
|
177
|
+
throw new CliArgumentsError(`"${profile}" is not a profile name: use letters, digits, ".", "_" or "-", up to 32.`);
|
|
178
|
+
}
|
|
179
|
+
text = text.slice(named[0].length).trim();
|
|
180
|
+
}
|
|
181
|
+
const withProfile = (request) => (profile ? { ...request, profile } : request);
|
|
171
182
|
if (!text)
|
|
172
|
-
return { words: [] };
|
|
183
|
+
return withProfile({ words: [] });
|
|
173
184
|
if (text.startsWith('-'))
|
|
174
|
-
return { words: splitCliArguments(text) };
|
|
175
|
-
return { words: [], prompt: text };
|
|
185
|
+
return withProfile({ words: splitCliArguments(text) });
|
|
186
|
+
return withProfile({ words: [], prompt: text });
|
|
176
187
|
}
|
|
177
188
|
export const WORKSPACE_MCP_SERVER_NAME = 'ztc';
|
|
178
189
|
/**
|
|
@@ -28,6 +28,19 @@ export interface OpenAgentPaneOptions {
|
|
|
28
28
|
server?: PaneProgram | Error | null;
|
|
29
29
|
/** Source of SHELL and the workspace variables (default: this process). */
|
|
30
30
|
env?: NodeJS.ProcessEnv;
|
|
31
|
+
/**
|
|
32
|
+
* The `motsi` to start the pane through, so Zerg Router's Command Central
|
|
33
|
+
* shows it; null for none. Default: when MotSi's remote view is on and
|
|
34
|
+
* ZTC's `panes.remoteView` isn't false.
|
|
35
|
+
*/
|
|
36
|
+
motsi?: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* A ZTC sign-in profile to start the CLI on; null for the CLI's own sign-in.
|
|
39
|
+
* Default: the request's `@profile`, else `panes.agentProfiles[cli]`.
|
|
40
|
+
*/
|
|
41
|
+
profile?: string | null;
|
|
42
|
+
/** Home directory the profiles live under (default: the user's). */
|
|
43
|
+
profilesHome?: string;
|
|
31
44
|
}
|
|
32
45
|
export interface OpenAgentPaneResult {
|
|
33
46
|
paneId?: number;
|
|
@@ -10,6 +10,9 @@ import { randomUUID } from 'node:crypto';
|
|
|
10
10
|
import { listWorkspacePanes, waitForPaneCreatedOrError } from '../agent/tools/pane.js';
|
|
11
11
|
import { AGENT_CLIS, agentCliArgs, agentPaneEnv, findAgentCli, isAgentCliId, loginShellExec, nextPaneTitle, workspaceMcpProgram, } from './agent_cli.js';
|
|
12
12
|
import { sessionNamePaneTitle } from './labels.js';
|
|
13
|
+
import { MOTSI_PANE_ENV, remoteViewMotsi, throughMotsi } from './motsi.js';
|
|
14
|
+
import { agentProfileEnv, ensureAgentProfile } from './agent_profiles.js';
|
|
15
|
+
import { configStore } from '../config.js';
|
|
13
16
|
/** Request ids of agent pane launches start with this, so the overseer can tell them apart. */
|
|
14
17
|
export const AGENT_PANE_REQUEST_PREFIX = 'agent-pane:';
|
|
15
18
|
/** Which agent CLI a `create_pane` request id launched, if any. */
|
|
@@ -36,8 +39,30 @@ export async function openAgentPane(transport, options) {
|
|
|
36
39
|
const env = options.env ?? process.env;
|
|
37
40
|
const server = options.server === undefined ? workspaceMcpProgram() : options.server;
|
|
38
41
|
const attached = server && !(server instanceof Error) ? server : null;
|
|
39
|
-
const
|
|
40
|
-
|
|
42
|
+
const profile = options.profile !== undefined
|
|
43
|
+
? options.profile
|
|
44
|
+
: options.request?.profile ?? configStore.get().panes?.agentProfiles?.[options.cli] ?? null;
|
|
45
|
+
let profileEnv = {};
|
|
46
|
+
if (profile) {
|
|
47
|
+
try {
|
|
48
|
+
profileEnv = agentProfileEnv(options.cli, ensureAgentProfile(options.cli, profile, options.profilesHome));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return { error: `Could not prepare the "${profile}" profile for ${spec.label}: ${error.message}` };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const label = profile ? `${spec.label} (${profile})` : spec.label;
|
|
55
|
+
const title = options.title?.trim() || nextPaneTitle(label, await existingTitles(transport));
|
|
56
|
+
const cliArgs = [
|
|
57
|
+
// On a profile, Codex keeps the sign-in in the profile's files, never a keyring entry other terminals share.
|
|
58
|
+
...(profile && options.cli === 'codex' ? ['-c', 'cli_auth_credentials_store="file"'] : []),
|
|
59
|
+
...agentCliArgs(options.cli, options.request ?? { words: [] }, attached),
|
|
60
|
+
];
|
|
61
|
+
const motsi = options.motsi === undefined ? remoteViewMotsi(configStore.get().panes, { env }) : options.motsi;
|
|
62
|
+
// Through MotSi, the login shell still runs first, then `motsi run` around the CLI.
|
|
63
|
+
const program = motsi
|
|
64
|
+
? loginShellExec(motsi, throughMotsi(motsi, title, { command: binary, args: cliArgs }).args, env.SHELL)
|
|
65
|
+
: loginShellExec(binary, cliArgs, env.SHELL);
|
|
41
66
|
const requestId = `${AGENT_PANE_REQUEST_PREFIX}${options.cli}:${randomUUID()}`;
|
|
42
67
|
const created = waitForPaneCreatedOrError(transport, { requestId }, 15000);
|
|
43
68
|
const sent = transport.send({
|
|
@@ -52,7 +77,7 @@ export async function openAgentPane(transport, options) {
|
|
|
52
77
|
...(options.ratio !== undefined ? { ratio: options.ratio } : {}),
|
|
53
78
|
...(options.target !== undefined ? { target: options.target } : {}),
|
|
54
79
|
...(options.newWindow ? { new_window: true } : {}),
|
|
55
|
-
env: agentPaneEnv(env),
|
|
80
|
+
env: { ...agentPaneEnv(env), ...(motsi ? MOTSI_PANE_ENV : {}), ...profileEnv },
|
|
56
81
|
});
|
|
57
82
|
if (!sent) {
|
|
58
83
|
created.catch(() => { });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZTC's own sign-in profiles for Claude Code and Codex panes.
|
|
3
|
+
*
|
|
4
|
+
* A profile is a config directory of its own, handed to the CLI through its
|
|
5
|
+
* documented variable (Claude Code's CLAUDE_CONFIG_DIR, Codex's CODEX_HOME).
|
|
6
|
+
* The user signs in there with the CLI's own login the first time a pane opens
|
|
7
|
+
* on it, and the CLI keeps that sign-in there, so a ZTC pane can use another
|
|
8
|
+
* account than the CLI uses in iTerm or Ghostty without changing it there.
|
|
9
|
+
* ZTC creates the directory and never reads what the CLI stores in it, and it
|
|
10
|
+
* never moves a pane to another profile on its own: the user picks.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentCliId } from './agent_cli.js';
|
|
13
|
+
/** Letters, digits, `.`, `_` and `-`, up to 32, starting with a letter or digit. */
|
|
14
|
+
export declare const PROFILE_NAME: RegExp;
|
|
15
|
+
export declare function isProfileName(value: string): boolean;
|
|
16
|
+
export declare function agentProfileDir(cli: AgentCliId, name: string, home?: string): string;
|
|
17
|
+
/** The profile's directory, created private to the user if it is new. */
|
|
18
|
+
export declare function ensureAgentProfile(cli: AgentCliId, name: string, home?: string): string;
|
|
19
|
+
/** The CLI's profiles, by name. */
|
|
20
|
+
export declare function listAgentProfiles(cli: AgentCliId, home?: string): string[];
|
|
21
|
+
/** The variable that points the CLI at the profile. */
|
|
22
|
+
export declare function agentProfileEnv(cli: AgentCliId, dir: string): Record<string, string>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZTC's own sign-in profiles for Claude Code and Codex panes.
|
|
3
|
+
*
|
|
4
|
+
* A profile is a config directory of its own, handed to the CLI through its
|
|
5
|
+
* documented variable (Claude Code's CLAUDE_CONFIG_DIR, Codex's CODEX_HOME).
|
|
6
|
+
* The user signs in there with the CLI's own login the first time a pane opens
|
|
7
|
+
* on it, and the CLI keeps that sign-in there, so a ZTC pane can use another
|
|
8
|
+
* account than the CLI uses in iTerm or Ghostty without changing it there.
|
|
9
|
+
* ZTC creates the directory and never reads what the CLI stores in it, and it
|
|
10
|
+
* never moves a pane to another profile on its own: the user picks.
|
|
11
|
+
*/
|
|
12
|
+
import { mkdirSync, readdirSync } from 'node:fs';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
/** Letters, digits, `.`, `_` and `-`, up to 32, starting with a letter or digit. */
|
|
16
|
+
export const PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/;
|
|
17
|
+
export function isProfileName(value) {
|
|
18
|
+
return PROFILE_NAME.test(value) && value !== '.' && value !== '..';
|
|
19
|
+
}
|
|
20
|
+
function profilesRoot(cli, home) {
|
|
21
|
+
return join(home, '.ztc', 'agent-profiles', cli);
|
|
22
|
+
}
|
|
23
|
+
export function agentProfileDir(cli, name, home = homedir()) {
|
|
24
|
+
if (!isProfileName(name))
|
|
25
|
+
throw new Error(`"${name}" is not a profile name.`);
|
|
26
|
+
return join(profilesRoot(cli, home), name);
|
|
27
|
+
}
|
|
28
|
+
/** The profile's directory, created private to the user if it is new. */
|
|
29
|
+
export function ensureAgentProfile(cli, name, home = homedir()) {
|
|
30
|
+
const dir = agentProfileDir(cli, name, home);
|
|
31
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
32
|
+
return dir;
|
|
33
|
+
}
|
|
34
|
+
/** The CLI's profiles, by name. */
|
|
35
|
+
export function listAgentProfiles(cli, home = homedir()) {
|
|
36
|
+
try {
|
|
37
|
+
return readdirSync(profilesRoot(cli, home), { withFileTypes: true })
|
|
38
|
+
.filter(entry => entry.isDirectory() && isProfileName(entry.name))
|
|
39
|
+
.map(entry => entry.name)
|
|
40
|
+
.sort();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** The variable that points the CLI at the profile. */
|
|
47
|
+
export function agentProfileEnv(cli, dir) {
|
|
48
|
+
return cli === 'claude' ? { CLAUDE_CONFIG_DIR: dir } : { CODEX_HOME: dir };
|
|
49
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panes Zerg Router can show, through MotSi.
|
|
3
|
+
*
|
|
4
|
+
* MotSi's `motsi run -- <command>` runs a program in a pseudo-terminal it
|
|
5
|
+
* passes through unchanged and, while the owner watches it in Zerg Router's
|
|
6
|
+
* Command Central, streams it there, redacted on this computer first. Whether
|
|
7
|
+
* a viewer may type is MotSi's to decide, and it allows it only into an agent
|
|
8
|
+
* (Claude Code, Codex, ZTC), never a shell.
|
|
9
|
+
*
|
|
10
|
+
* ZTC starts its panes through it when MotSi is installed and MotSi's own
|
|
11
|
+
* remote view switch is on (`motsi remote view on|off`, kept on this computer),
|
|
12
|
+
* unless ZTC's `panes.remoteView` setting is false. ZTC reads only that switch
|
|
13
|
+
* from MotSi's settings, nothing else of MotSi's.
|
|
14
|
+
*/
|
|
15
|
+
import type { PaneProgram } from './agent_cli.js';
|
|
16
|
+
export interface MotsiLookup {
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
home?: string;
|
|
19
|
+
systemDirs?: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/** The `motsi` binary: PATH first, then `go install`'s and the installers' directories. */
|
|
22
|
+
export declare function findMotsi(lookup?: MotsiLookup): string | null;
|
|
23
|
+
/** MotSi's own switch, from its settings (`$MOTSI_HOME` or `~/.config/motsi`). Anything but a true is off. */
|
|
24
|
+
export declare function motsiRemoteViewOn(lookup?: MotsiLookup): boolean;
|
|
25
|
+
/** The `motsi` to start panes through, or null when panes start as they always have. */
|
|
26
|
+
export declare function remoteViewMotsi(panes: {
|
|
27
|
+
remoteView?: boolean;
|
|
28
|
+
} | undefined, lookup?: MotsiLookup): string | null;
|
|
29
|
+
/** The same program and arguments, as a MotSi terminal named after the pane. */
|
|
30
|
+
export declare function throughMotsi(motsi: string, title: string, program: PaneProgram): PaneProgram;
|
|
31
|
+
/**
|
|
32
|
+
* Pane environment for a MotSi terminal: an inherited terminal id would make
|
|
33
|
+
* `motsi run` treat the pane as part of the terminal ZTC itself runs in.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MOTSI_PANE_ENV: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panes Zerg Router can show, through MotSi.
|
|
3
|
+
*
|
|
4
|
+
* MotSi's `motsi run -- <command>` runs a program in a pseudo-terminal it
|
|
5
|
+
* passes through unchanged and, while the owner watches it in Zerg Router's
|
|
6
|
+
* Command Central, streams it there, redacted on this computer first. Whether
|
|
7
|
+
* a viewer may type is MotSi's to decide, and it allows it only into an agent
|
|
8
|
+
* (Claude Code, Codex, ZTC), never a shell.
|
|
9
|
+
*
|
|
10
|
+
* ZTC starts its panes through it when MotSi is installed and MotSi's own
|
|
11
|
+
* remote view switch is on (`motsi remote view on|off`, kept on this computer),
|
|
12
|
+
* unless ZTC's `panes.remoteView` setting is false. ZTC reads only that switch
|
|
13
|
+
* from MotSi's settings, nothing else of MotSi's.
|
|
14
|
+
*/
|
|
15
|
+
import { accessSync, constants, readFileSync, statSync } from 'node:fs';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
const SYSTEM_BIN_DIRS = ['/opt/homebrew/bin', '/usr/local/bin', '/usr/bin'];
|
|
19
|
+
function isExecutableFile(path) {
|
|
20
|
+
try {
|
|
21
|
+
if (!statSync(path).isFile())
|
|
22
|
+
return false;
|
|
23
|
+
accessSync(path, constants.X_OK);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** The `motsi` binary: PATH first, then `go install`'s and the installers' directories. */
|
|
31
|
+
export function findMotsi(lookup = {}) {
|
|
32
|
+
const env = lookup.env ?? process.env;
|
|
33
|
+
const home = lookup.home ?? homedir();
|
|
34
|
+
const dirs = [
|
|
35
|
+
...(env.PATH ?? '').split(':').filter(Boolean),
|
|
36
|
+
join(home, 'go', 'bin'),
|
|
37
|
+
join(home, '.local', 'bin'),
|
|
38
|
+
...(lookup.systemDirs ?? SYSTEM_BIN_DIRS),
|
|
39
|
+
];
|
|
40
|
+
for (const dir of dirs) {
|
|
41
|
+
const candidate = join(dir, 'motsi');
|
|
42
|
+
if (isExecutableFile(candidate))
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/** MotSi's own switch, from its settings (`$MOTSI_HOME` or `~/.config/motsi`). Anything but a true is off. */
|
|
48
|
+
export function motsiRemoteViewOn(lookup = {}) {
|
|
49
|
+
const env = lookup.env ?? process.env;
|
|
50
|
+
const dir = env.MOTSI_HOME || join(lookup.home ?? homedir(), '.config', 'motsi');
|
|
51
|
+
try {
|
|
52
|
+
const settings = JSON.parse(readFileSync(join(dir, 'settings.json'), 'utf8'));
|
|
53
|
+
return settings?.remote_view === true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** The `motsi` to start panes through, or null when panes start as they always have. */
|
|
60
|
+
export function remoteViewMotsi(panes, lookup = {}) {
|
|
61
|
+
if (panes?.remoteView === false)
|
|
62
|
+
return null;
|
|
63
|
+
if (!motsiRemoteViewOn(lookup))
|
|
64
|
+
return null;
|
|
65
|
+
return findMotsi(lookup);
|
|
66
|
+
}
|
|
67
|
+
/** The same program and arguments, as a MotSi terminal named after the pane. */
|
|
68
|
+
export function throughMotsi(motsi, title, program) {
|
|
69
|
+
return { command: motsi, args: ['run', `--title=${title}`, '--', program.command, ...program.args] };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Pane environment for a MotSi terminal: an inherited terminal id would make
|
|
73
|
+
* `motsi run` treat the pane as part of the terminal ZTC itself runs in.
|
|
74
|
+
*/
|
|
75
|
+
export const MOTSI_PANE_ENV = Object.freeze({ MOTSI_TERMINAL_ID: '' });
|
package/dist/zlm-embed.mjs
CHANGED
|
@@ -78502,7 +78502,7 @@ var invokeSkillTool = makeInvokeSkillTool(resolveAmbientSkills);
|
|
|
78502
78502
|
// src/zlm.ts
|
|
78503
78503
|
var ZLM_EMBED_API_VERSION = "zerglang.ztc-embed/1";
|
|
78504
78504
|
var ZLM_STDIO_PROTOCOL = "zerglang.ztc-host/1";
|
|
78505
|
-
var ZLM_ZTC_VERSION = true ? "0.10.
|
|
78505
|
+
var ZLM_ZTC_VERSION = true ? "0.10.1-dev.202611052216" : "development";
|
|
78506
78506
|
var ZLM_MAX_HOST_TOOLS = 64;
|
|
78507
78507
|
var ZLM_MAX_SYSTEM_PROMPT_BYTES = 128 * 1024;
|
|
78508
78508
|
var ZLM_HOST_APPROVAL_CAPABILITIES = Object.freeze([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zergai/ztc",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1-dev.202611052216",
|
|
4
4
|
"description": "Zerg Terminal Client - CLI agent for continual AI systems",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"homepage": "https://zergai.com/docs/ztc",
|
|
@@ -275,13 +275,13 @@
|
|
|
275
275
|
"node": "^20.19.0 || >=22.12.0"
|
|
276
276
|
},
|
|
277
277
|
"zergRelease": {
|
|
278
|
-
"branch": "
|
|
279
|
-
"channel": "
|
|
280
|
-
"sourceCommit": "
|
|
278
|
+
"branch": "development",
|
|
279
|
+
"channel": "dev",
|
|
280
|
+
"sourceCommit": "eedc94d57cd6224856beab9b16b5d6c235978ece",
|
|
281
281
|
"sourceVersion": "0.2.0-canary.2"
|
|
282
282
|
},
|
|
283
283
|
"publishConfig": {
|
|
284
|
-
"tag": "
|
|
284
|
+
"tag": "dev",
|
|
285
285
|
"access": "public"
|
|
286
286
|
}
|
|
287
287
|
}
|