ftown-bridge 0.3.15 → 0.4.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/bin/ftown-sessions +5 -0
- package/dist/agent-commands.d.ts +17 -0
- package/dist/agent-commands.js +41 -0
- package/dist/agent-commands.js.map +1 -0
- package/dist/claude-runner.d.ts +27 -0
- package/dist/claude-runner.js +234 -44
- package/dist/claude-runner.js.map +1 -1
- package/dist/create-ftown-session.d.ts +49 -0
- package/dist/create-ftown-session.js +147 -0
- package/dist/create-ftown-session.js.map +1 -0
- package/dist/cursor-hook-installer.js +2 -2
- package/dist/cursor-hook-installer.js.map +1 -1
- package/dist/ftown-sessions-cli.d.ts +2 -0
- package/dist/ftown-sessions-cli.js +304 -0
- package/dist/ftown-sessions-cli.js.map +1 -0
- package/dist/harness-cli.d.ts +2 -0
- package/dist/harness-cli.js +432 -0
- package/dist/harness-cli.js.map +1 -0
- package/dist/harness-format.d.ts +3 -0
- package/dist/harness-format.js +14 -0
- package/dist/harness-format.js.map +1 -0
- package/dist/harness-installer.d.ts +16 -0
- package/dist/harness-installer.js +108 -0
- package/dist/harness-installer.js.map +1 -0
- package/dist/hook-installer.js +2 -2
- package/dist/hook-installer.js.map +1 -1
- package/dist/index.js +265 -61
- package/dist/index.js.map +1 -1
- package/dist/install-ftown-cli.d.ts +3 -0
- package/dist/install-ftown-cli.js +20 -0
- package/dist/install-ftown-cli.js.map +1 -0
- package/dist/install-ftown-skill.d.ts +4 -0
- package/dist/install-ftown-skill.js +50 -0
- package/dist/install-ftown-skill.js.map +1 -0
- package/dist/install-notify-script.d.ts +6 -0
- package/dist/install-notify-script.js +23 -0
- package/dist/install-notify-script.js.map +1 -0
- package/dist/local-api-server.d.ts +5 -0
- package/dist/local-api-server.js +137 -6
- package/dist/local-api-server.js.map +1 -1
- package/dist/session-registry.d.ts +11 -1
- package/dist/session-registry.js +3 -3
- package/dist/session-registry.js.map +1 -1
- package/dist/terminal-manager.d.ts +3 -1
- package/dist/terminal-manager.js +13 -6
- package/dist/terminal-manager.js.map +1 -1
- package/dist/tmux.d.ts +24 -0
- package/dist/tmux.js +149 -0
- package/dist/tmux.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/hooks/notify.sh +34 -12
- package/package.json +9 -4
- package/skills/ftown-sessions/SKILL.md +136 -0
- package/skills/ftown-sessions/scripts/ftown-sessions +4 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { buildSessionCommand } from './agent-commands.js';
|
|
3
|
+
import { installProjectCursorHooks } from './cursor-hook-installer.js';
|
|
4
|
+
import { registerSessionWorkspace } from './session-registry.js';
|
|
5
|
+
export async function resolveParentSessionId(store, parentSessionId) {
|
|
6
|
+
if (!parentSessionId)
|
|
7
|
+
return undefined;
|
|
8
|
+
const proposed = await store.loadSession(parentSessionId);
|
|
9
|
+
if (!proposed) {
|
|
10
|
+
throw new Error('Parent session not found');
|
|
11
|
+
}
|
|
12
|
+
return proposed.parentSessionId ?? proposed.id;
|
|
13
|
+
}
|
|
14
|
+
/** One compact paragraph injected into a child agent's first input. */
|
|
15
|
+
export function buildChildBriefing(params) {
|
|
16
|
+
return (`[ftown] You are child session '${params.childName}' (${params.childId}), ` +
|
|
17
|
+
`spawned by parent '${params.parentName}' (${params.parentId}). ` +
|
|
18
|
+
`Report results/questions to your parent with: ~/.ftown/ftown-sessions tell --parent "<message>" ` +
|
|
19
|
+
`— message siblings with tell --siblings, and inspect peers with ` +
|
|
20
|
+
`~/.ftown/ftown-sessions list / screen <id>. Your parent can read your terminal at any time.`);
|
|
21
|
+
}
|
|
22
|
+
/** One compact paragraph injected into an orchestrator agent's first input. */
|
|
23
|
+
export function buildOrchestratorBriefing(params) {
|
|
24
|
+
return (`[ftown] You are running inside ftown session '${params.sessionName}' ` +
|
|
25
|
+
`(${params.sessionId}) and can orchestrate sibling agent sessions on this machine. ` +
|
|
26
|
+
`Spawn workers with: ~/.ftown/ftown-sessions create --shell claude|cursor|shell ` +
|
|
27
|
+
`--parent --workdir <dir> --name <name> --prompt "<task>" — children are ` +
|
|
28
|
+
`automatically briefed to report back to you via tell, and their reports arrive in ` +
|
|
29
|
+
`your terminal as messages starting with [ftown msg from <name>]. Inspect any session ` +
|
|
30
|
+
`with ~/.ftown/ftown-sessions list / screen <id> / grep <id> --pattern <re>, and ` +
|
|
31
|
+
`message one with tell <id> "<text>".`);
|
|
32
|
+
}
|
|
33
|
+
export async function createFtownSession(deps, input) {
|
|
34
|
+
const command = buildSessionCommand(input);
|
|
35
|
+
const prompt = input.prompt?.trim() ?? '';
|
|
36
|
+
let parentSessionId;
|
|
37
|
+
let parentName;
|
|
38
|
+
if (input.parentSessionId) {
|
|
39
|
+
parentSessionId = await resolveParentSessionId(deps.store, input.parentSessionId);
|
|
40
|
+
if (parentSessionId) {
|
|
41
|
+
const parentSession = await deps.store.loadSession(parentSessionId);
|
|
42
|
+
parentName = parentSession?.name ?? parentSessionId.slice(0, 8);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const sessionId = uuidv4();
|
|
46
|
+
const session = {
|
|
47
|
+
id: sessionId,
|
|
48
|
+
name: input.name ?? (prompt || command).slice(0, 80),
|
|
49
|
+
command,
|
|
50
|
+
prompt: prompt || undefined,
|
|
51
|
+
status: 'running',
|
|
52
|
+
bridgeId: deps.bridgeId,
|
|
53
|
+
createdAt: new Date().toISOString(),
|
|
54
|
+
updatedAt: new Date().toISOString(),
|
|
55
|
+
workingDir: input.workingDir,
|
|
56
|
+
shellType: input.shellType,
|
|
57
|
+
model: input.model,
|
|
58
|
+
claudeSessionId: input.claudeSessionId,
|
|
59
|
+
cursorSessionId: input.cursorSessionId,
|
|
60
|
+
env: input.env,
|
|
61
|
+
parentSessionId,
|
|
62
|
+
runtime: deps.runner.getPreferredRuntime(),
|
|
63
|
+
};
|
|
64
|
+
await deps.store.saveSession(session);
|
|
65
|
+
await deps.centrifugo.publishSessionUpdate(deps.userId, session);
|
|
66
|
+
registerSessionWorkspace(sessionId, input.workingDir);
|
|
67
|
+
if (input.shellType === 'cursor' && input.workingDir) {
|
|
68
|
+
installProjectCursorHooks(input.workingDir, deps.notifyScriptPath);
|
|
69
|
+
}
|
|
70
|
+
// Agent sessions (anything but a plain 'shell') spawned by a parent get a
|
|
71
|
+
// one-paragraph briefing prepended to their first input so they know their
|
|
72
|
+
// place in the session tree and how to talk to parent/siblings.
|
|
73
|
+
const isAgent = input.shellType !== 'shell';
|
|
74
|
+
const childBriefing = parentSessionId && parentName && isAgent
|
|
75
|
+
? buildChildBriefing({
|
|
76
|
+
childName: session.name,
|
|
77
|
+
childId: sessionId,
|
|
78
|
+
parentName,
|
|
79
|
+
parentId: parentSessionId,
|
|
80
|
+
})
|
|
81
|
+
: undefined;
|
|
82
|
+
const orchestratorBriefing = input.orchestrator && isAgent
|
|
83
|
+
? buildOrchestratorBriefing({ sessionName: session.name, sessionId })
|
|
84
|
+
: undefined;
|
|
85
|
+
// Orchestrator paragraph follows the child paragraph, separated by a blank line.
|
|
86
|
+
const briefing = [childBriefing, orchestratorBriefing].filter(Boolean).join('\n\n') || undefined;
|
|
87
|
+
// Composer TUIs need the submit CR sent separately after the paste settles —
|
|
88
|
+
// a CR inside the pasted chunk becomes a newline, and ESC+CR reads as
|
|
89
|
+
// Alt+Enter (newline) on current Claude Code, so plain CR it is.
|
|
90
|
+
const promptSubmitSuffix = '\r';
|
|
91
|
+
let initialInput;
|
|
92
|
+
let initialInputDelay;
|
|
93
|
+
let submitSuffix;
|
|
94
|
+
if (briefing) {
|
|
95
|
+
initialInput = prompt ? `${briefing}\n\nTask: ${prompt}` : briefing;
|
|
96
|
+
initialInputDelay = input.initialInputDelay ?? 2000;
|
|
97
|
+
submitSuffix = promptSubmitSuffix;
|
|
98
|
+
}
|
|
99
|
+
else if (input.initialInput !== undefined) {
|
|
100
|
+
// Raw passthrough: callers own the submit keystrokes; suppress the default CR.
|
|
101
|
+
initialInput = input.initialInput;
|
|
102
|
+
initialInputDelay = input.initialInputDelay;
|
|
103
|
+
submitSuffix = '';
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
initialInput = prompt || undefined;
|
|
107
|
+
initialInputDelay = input.initialInputDelay ?? (prompt ? 2000 : undefined);
|
|
108
|
+
submitSuffix = promptSubmitSuffix;
|
|
109
|
+
}
|
|
110
|
+
deps.runner.run(sessionId, command, {
|
|
111
|
+
workingDir: input.workingDir,
|
|
112
|
+
env: input.env,
|
|
113
|
+
initialInput,
|
|
114
|
+
initialInputDelay,
|
|
115
|
+
submitSuffix,
|
|
116
|
+
hookPort: deps.hookPort,
|
|
117
|
+
hookToken: deps.hookToken,
|
|
118
|
+
parentSessionId,
|
|
119
|
+
});
|
|
120
|
+
deps.wireTerminalInput(sessionId);
|
|
121
|
+
return session;
|
|
122
|
+
}
|
|
123
|
+
/** Map a Local API JSON body into create input. */
|
|
124
|
+
export function parseCreateSessionBody(body, callerSessionId) {
|
|
125
|
+
const shellType = body.shellType;
|
|
126
|
+
const env = body.env;
|
|
127
|
+
let parentSessionId = typeof body.parentSessionId === 'string' ? body.parentSessionId : undefined;
|
|
128
|
+
if (!parentSessionId && callerSessionId) {
|
|
129
|
+
parentSessionId = callerSessionId;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
command: typeof body.command === 'string' ? body.command : undefined,
|
|
133
|
+
prompt: typeof body.prompt === 'string' ? body.prompt : undefined,
|
|
134
|
+
name: typeof body.name === 'string' ? body.name : undefined,
|
|
135
|
+
workingDir: typeof body.workingDir === 'string' ? body.workingDir : undefined,
|
|
136
|
+
shellType,
|
|
137
|
+
model: typeof body.model === 'string' ? body.model : undefined,
|
|
138
|
+
claudeSessionId: typeof body.claudeSessionId === 'string' ? body.claudeSessionId : undefined,
|
|
139
|
+
cursorSessionId: typeof body.cursorSessionId === 'string' ? body.cursorSessionId : undefined,
|
|
140
|
+
env: env && typeof env === 'object' ? env : undefined,
|
|
141
|
+
parentSessionId,
|
|
142
|
+
initialInput: typeof body.initialInput === 'string' ? body.initialInput : undefined,
|
|
143
|
+
initialInputDelay: typeof body.initialInputDelay === 'number' ? body.initialInputDelay : undefined,
|
|
144
|
+
orchestrator: body.orchestrator === true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=create-ftown-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ftown-session.js","sourceRoot":"","sources":["../src/create-ftown-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ const CURSOR_HOOK_EVENTS = [
|
|
|
12
12
|
'stop',
|
|
13
13
|
'beforeSubmitPrompt',
|
|
14
14
|
];
|
|
15
|
-
|
|
15
|
+
import { isFtownNotifyCommand } from './install-notify-script.js';
|
|
16
16
|
function isObject(value) {
|
|
17
17
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
18
18
|
}
|
|
@@ -62,7 +62,7 @@ function mergeHooksFile(settingsPath, notifyScriptPath, label) {
|
|
|
62
62
|
hooks[event] = list;
|
|
63
63
|
const foundIndex = list.findIndex((entry) => isObject(entry) &&
|
|
64
64
|
typeof entry.command === 'string' &&
|
|
65
|
-
entry.command
|
|
65
|
+
isFtownNotifyCommand(entry.command));
|
|
66
66
|
if (foundIndex === -1) {
|
|
67
67
|
list.push({ command: notifyScriptPath });
|
|
68
68
|
added++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-hook-installer.js","sourceRoot":"","sources":["../src/cursor-hook-installer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,oFAAoF;AACpF,MAAM,kBAAkB,GAAG;IACzB,cAAc;IACd,YAAY;IACZ,aAAa;IACb,sBAAsB;IACtB,qBAAqB;IACrB,eAAe;IACf,MAAM;IACN,oBAAoB;CACZ,CAAC;AAEX,
|
|
1
|
+
{"version":3,"file":"cursor-hook-installer.js","sourceRoot":"","sources":["../src/cursor-hook-installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Local API client for ftown cross-session control.
|
|
4
|
+
* Installed to ~/.ftown/ftown-sessions by ftown-bridge.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
function loadBridge() {
|
|
10
|
+
const path = join(homedir(), '.ftown', 'bridge.json');
|
|
11
|
+
const raw = readFileSync(path, 'utf8');
|
|
12
|
+
const data = JSON.parse(raw);
|
|
13
|
+
if (!data.port || !data.token) {
|
|
14
|
+
throw new Error('Invalid bridge.json (missing port or token)');
|
|
15
|
+
}
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
async function api(method, path, body, extraHeaders) {
|
|
19
|
+
const { port, token } = loadBridge();
|
|
20
|
+
const headers = {
|
|
21
|
+
Authorization: `Bearer ${token}`,
|
|
22
|
+
...extraHeaders,
|
|
23
|
+
};
|
|
24
|
+
let payload;
|
|
25
|
+
if (body !== undefined) {
|
|
26
|
+
headers['Content-Type'] = 'application/json';
|
|
27
|
+
payload = JSON.stringify(body);
|
|
28
|
+
}
|
|
29
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
30
|
+
method,
|
|
31
|
+
headers,
|
|
32
|
+
body: payload,
|
|
33
|
+
});
|
|
34
|
+
const text = await res.text();
|
|
35
|
+
let data = null;
|
|
36
|
+
if (text) {
|
|
37
|
+
try {
|
|
38
|
+
data = JSON.parse(text);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
data = { raw: text };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
const err = data;
|
|
46
|
+
throw new Error(err?.error ?? `HTTP ${res.status}`);
|
|
47
|
+
}
|
|
48
|
+
return { status: res.status, data };
|
|
49
|
+
}
|
|
50
|
+
async function listSessionInfo() {
|
|
51
|
+
const { data } = await api('GET', '/api/sessions');
|
|
52
|
+
return data.sessions ?? [];
|
|
53
|
+
}
|
|
54
|
+
async function resolveFanout(mode, self) {
|
|
55
|
+
if (!self) {
|
|
56
|
+
throw new Error(`--${mode} requires FTOWN_SESSION_ID to be set`);
|
|
57
|
+
}
|
|
58
|
+
const sessions = await listSessionInfo();
|
|
59
|
+
const me = sessions.find((s) => s.id === self);
|
|
60
|
+
if (mode === 'parent') {
|
|
61
|
+
const parent = me?.parentSessionId;
|
|
62
|
+
if (!parent)
|
|
63
|
+
throw new Error('Current session has no parent');
|
|
64
|
+
return [parent];
|
|
65
|
+
}
|
|
66
|
+
if (mode === 'children') {
|
|
67
|
+
return sessions.filter((s) => s.parentSessionId === self).map((s) => s.id);
|
|
68
|
+
}
|
|
69
|
+
// siblings
|
|
70
|
+
const parent = me?.parentSessionId;
|
|
71
|
+
if (!parent)
|
|
72
|
+
throw new Error('Current session has no parent (cannot resolve siblings)');
|
|
73
|
+
return sessions
|
|
74
|
+
.filter((s) => s.parentSessionId === parent && s.id !== self)
|
|
75
|
+
.map((s) => s.id);
|
|
76
|
+
}
|
|
77
|
+
function flag(args, name) {
|
|
78
|
+
const i = args.indexOf(name);
|
|
79
|
+
if (i === -1 || i + 1 >= args.length)
|
|
80
|
+
return undefined;
|
|
81
|
+
return args[i + 1];
|
|
82
|
+
}
|
|
83
|
+
function hasFlag(args, name) {
|
|
84
|
+
return args.includes(name);
|
|
85
|
+
}
|
|
86
|
+
function usage() {
|
|
87
|
+
console.error(`Usage: ftown-sessions <command> [options]
|
|
88
|
+
|
|
89
|
+
Commands:
|
|
90
|
+
list List sessions
|
|
91
|
+
create [options] Spawn a new agent session
|
|
92
|
+
get <session-id> Session metadata
|
|
93
|
+
screen <session-id> Print terminal lines
|
|
94
|
+
grep <session-id> Search terminal output
|
|
95
|
+
keys <session-id> <text> Send keys to a running session
|
|
96
|
+
tell <target> <message...> Send a text message to another session's terminal
|
|
97
|
+
running <session-id> Check if session PTY is running
|
|
98
|
+
|
|
99
|
+
Tell targets (one of):
|
|
100
|
+
<session-id> Explicit target session id
|
|
101
|
+
--parent Message FTOWN_SESSION_ID's parent session
|
|
102
|
+
--children Message all sessions parented to FTOWN_SESSION_ID
|
|
103
|
+
--siblings Message sessions sharing my parent (excluding me)
|
|
104
|
+
|
|
105
|
+
Create options:
|
|
106
|
+
--shell <type> cursor | claude | shell | opencode (default: claude)
|
|
107
|
+
--prompt <text> Initial message
|
|
108
|
+
--workdir <path> Working directory
|
|
109
|
+
--name <label> Dashboard name
|
|
110
|
+
--command <cmd> Full command override
|
|
111
|
+
--parent Link parent to FTOWN_SESSION_ID or X-Ftown-Session-Id
|
|
112
|
+
--parent-id <uuid> Explicit parent session id
|
|
113
|
+
--orchestrator Brief the new agent to spawn and coordinate sibling sessions
|
|
114
|
+
--model <name> Model (cursor)
|
|
115
|
+
--json Output raw JSON (default for most commands)
|
|
116
|
+
|
|
117
|
+
Screen/grep options:
|
|
118
|
+
--offset <n> Pagination offset (default: 0)
|
|
119
|
+
--limit <n> Max lines/matches (default: 200)
|
|
120
|
+
|
|
121
|
+
Grep options:
|
|
122
|
+
--pattern <regex> Required for grep
|
|
123
|
+
|
|
124
|
+
Reads ~/.ftown/bridge.json (ftown-bridge must be running).`);
|
|
125
|
+
}
|
|
126
|
+
async function main() {
|
|
127
|
+
const argv = process.argv.slice(2);
|
|
128
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
|
|
129
|
+
usage();
|
|
130
|
+
process.exit(argv.length === 0 ? 1 : 0);
|
|
131
|
+
}
|
|
132
|
+
const cmd = argv[0];
|
|
133
|
+
const rest = argv.slice(1);
|
|
134
|
+
const jsonOut = !hasFlag(rest, '--plain');
|
|
135
|
+
try {
|
|
136
|
+
switch (cmd) {
|
|
137
|
+
case 'list': {
|
|
138
|
+
const { data } = await api('GET', '/api/sessions');
|
|
139
|
+
console.log(jsonOut ? JSON.stringify(data, null, 2) : formatSessionList(data));
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'create': {
|
|
143
|
+
const shellType = flag(rest, '--shell');
|
|
144
|
+
const prompt = flag(rest, '--prompt');
|
|
145
|
+
const workingDir = flag(rest, '--workdir');
|
|
146
|
+
const name = flag(rest, '--name');
|
|
147
|
+
const command = flag(rest, '--command');
|
|
148
|
+
const model = flag(rest, '--model');
|
|
149
|
+
const parentId = flag(rest, '--parent-id');
|
|
150
|
+
const useParent = hasFlag(rest, '--parent');
|
|
151
|
+
const orchestrator = hasFlag(rest, '--orchestrator');
|
|
152
|
+
const body = {};
|
|
153
|
+
if (shellType)
|
|
154
|
+
body.shellType = shellType;
|
|
155
|
+
if (prompt)
|
|
156
|
+
body.prompt = prompt;
|
|
157
|
+
if (workingDir)
|
|
158
|
+
body.workingDir = workingDir;
|
|
159
|
+
if (name)
|
|
160
|
+
body.name = name;
|
|
161
|
+
if (command)
|
|
162
|
+
body.command = command;
|
|
163
|
+
if (model)
|
|
164
|
+
body.model = model;
|
|
165
|
+
if (parentId)
|
|
166
|
+
body.parentSessionId = parentId;
|
|
167
|
+
else if (useParent)
|
|
168
|
+
body.parentSessionId = true;
|
|
169
|
+
if (orchestrator)
|
|
170
|
+
body.orchestrator = true;
|
|
171
|
+
const caller = process.env.FTOWN_SESSION_ID?.trim();
|
|
172
|
+
const headers = useParent && caller ? { 'X-Ftown-Session-Id': caller } : undefined;
|
|
173
|
+
const { data } = await api('POST', '/api/sessions', body, headers);
|
|
174
|
+
console.log(jsonOut ? JSON.stringify(data, null, 2) : formatCreated(data));
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case 'get': {
|
|
178
|
+
const id = rest.find((a) => !a.startsWith('--'));
|
|
179
|
+
if (!id)
|
|
180
|
+
throw new Error('Missing session-id');
|
|
181
|
+
const { data } = await api('GET', `/api/sessions/${id}`);
|
|
182
|
+
console.log(JSON.stringify(data, null, 2));
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case 'screen': {
|
|
186
|
+
const id = rest.find((a) => !a.startsWith('--'));
|
|
187
|
+
if (!id)
|
|
188
|
+
throw new Error('Missing session-id');
|
|
189
|
+
const offset = flag(rest, '--offset') ?? '0';
|
|
190
|
+
const limit = flag(rest, '--limit') ?? '200';
|
|
191
|
+
const { data } = await api('GET', `/api/sessions/${id}/screen?offset=${offset}&limit=${limit}`);
|
|
192
|
+
if (jsonOut) {
|
|
193
|
+
console.log(JSON.stringify(data, null, 2));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const lines = data.lines ?? [];
|
|
197
|
+
for (const line of lines)
|
|
198
|
+
console.log(line);
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'grep': {
|
|
203
|
+
const id = rest.find((a) => !a.startsWith('--'));
|
|
204
|
+
const pattern = flag(rest, '--pattern');
|
|
205
|
+
if (!id)
|
|
206
|
+
throw new Error('Missing session-id');
|
|
207
|
+
if (!pattern)
|
|
208
|
+
throw new Error('Missing --pattern');
|
|
209
|
+
const offset = parseInt(flag(rest, '--offset') ?? '0', 10);
|
|
210
|
+
const limit = parseInt(flag(rest, '--limit') ?? '100', 10);
|
|
211
|
+
const { data } = await api('POST', `/api/sessions/${id}/grep`, {
|
|
212
|
+
pattern,
|
|
213
|
+
offset,
|
|
214
|
+
limit,
|
|
215
|
+
});
|
|
216
|
+
console.log(JSON.stringify(data, null, 2));
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 'keys': {
|
|
220
|
+
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
221
|
+
const id = positional[0];
|
|
222
|
+
const keys = positional.slice(1).join(' ');
|
|
223
|
+
if (!id || !keys)
|
|
224
|
+
throw new Error('Usage: keys <session-id> <text>');
|
|
225
|
+
await api('POST', `/api/sessions/${id}/keys`, { keys });
|
|
226
|
+
console.log(JSON.stringify({ sent: true, sessionId: id }));
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case 'tell': {
|
|
230
|
+
const useParent = hasFlag(rest, '--parent');
|
|
231
|
+
const useChildren = hasFlag(rest, '--children');
|
|
232
|
+
const useSiblings = hasFlag(rest, '--siblings');
|
|
233
|
+
const fanCount = [useParent, useChildren, useSiblings].filter(Boolean).length;
|
|
234
|
+
if (fanCount > 1) {
|
|
235
|
+
throw new Error('Use only one of --parent, --children, --siblings');
|
|
236
|
+
}
|
|
237
|
+
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
238
|
+
const self = process.env.FTOWN_SESSION_ID?.trim();
|
|
239
|
+
let targets;
|
|
240
|
+
let message;
|
|
241
|
+
if (fanCount === 1) {
|
|
242
|
+
message = positional.join(' ');
|
|
243
|
+
targets = await resolveFanout(useParent ? 'parent' : useChildren ? 'children' : 'siblings', self);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const target = positional[0];
|
|
247
|
+
message = positional.slice(1).join(' ');
|
|
248
|
+
if (!target) {
|
|
249
|
+
throw new Error('Usage: tell <target|--parent|--children|--siblings> <message...>');
|
|
250
|
+
}
|
|
251
|
+
targets = [target];
|
|
252
|
+
}
|
|
253
|
+
if (!message)
|
|
254
|
+
throw new Error('Missing message');
|
|
255
|
+
if (targets.length === 0)
|
|
256
|
+
throw new Error('No matching target sessions');
|
|
257
|
+
for (const target of targets) {
|
|
258
|
+
const reqBody = { text: message };
|
|
259
|
+
if (self)
|
|
260
|
+
reqBody.from = self;
|
|
261
|
+
try {
|
|
262
|
+
const { data } = await api('POST', `/api/sessions/${target}/message`, reqBody);
|
|
263
|
+
console.log(JSON.stringify({ target, ...data }));
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
267
|
+
console.log(JSON.stringify({ target, error: msg }));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case 'running': {
|
|
273
|
+
const id = rest.find((a) => !a.startsWith('--'));
|
|
274
|
+
if (!id)
|
|
275
|
+
throw new Error('Missing session-id');
|
|
276
|
+
const { data } = await api('GET', `/api/sessions/${id}/running`);
|
|
277
|
+
console.log(JSON.stringify(data, null, 2));
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
default:
|
|
281
|
+
usage();
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
287
|
+
console.error(`ftown-sessions: ${msg}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function formatSessionList(data) {
|
|
292
|
+
const sessions = data.sessions ?? [];
|
|
293
|
+
return sessions
|
|
294
|
+
.map((s) => `${s.id} ${s.status} ${s.name ?? ''} ${s.workingDir ?? ''}`.trimEnd())
|
|
295
|
+
.join('\n');
|
|
296
|
+
}
|
|
297
|
+
function formatCreated(data) {
|
|
298
|
+
const session = data.session;
|
|
299
|
+
if (!session)
|
|
300
|
+
return JSON.stringify(data, null, 2);
|
|
301
|
+
return `created ${session.id} ${session.name} (${session.status})`;
|
|
302
|
+
}
|
|
303
|
+
main();
|
|
304
|
+
//# sourceMappingURL=ftown-sessions-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftown-sessions-cli.js","sourceRoot":"","sources":["../src/ftown-sessions-cli.ts"],"names":[],"mappings":"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"}
|