phewsh 0.12.0 → 0.12.2
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 +2 -1
- package/bin/phewsh.js +3 -1
- package/commands/serve.js +36 -17
- package/commands/update.js +79 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Connect the web app to your local machine for real-time task execution:
|
|
|
23
23
|
phewsh serve
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
This starts a bridge on `localhost:7483`. Open [phewsh.com/intent](https://phewsh.com/intent) and go to the Work tab — you'll see a green "Live" indicator. Agent tasks get a "Run Live" button that executes
|
|
26
|
+
This starts a bridge on `localhost:7483`. Open [phewsh.com/intent](https://phewsh.com/intent) and go to the Work tab — you'll see a green "Live" indicator. Agent tasks get a "Run Live" button that executes on your machine through whichever agent CLIs you have installed: **Claude Code, Codex, Gemini, Cursor Agent, OpenCode**. PHEWSH detects them automatically and every run leaves a receipt (`phewsh receipts`).
|
|
27
27
|
|
|
28
28
|
## Interactive Shell
|
|
29
29
|
|
|
@@ -67,6 +67,7 @@ phewsh pull # Sync cloud to local
|
|
|
67
67
|
phewsh mcp setup # Configure MCP server for agent connectivity
|
|
68
68
|
phewsh mcp serve # Coordination bridge — route work to live agents
|
|
69
69
|
phewsh receipts # Proof trail — what agents actually did, with evidence
|
|
70
|
+
phewsh update # Update phewsh to the latest version
|
|
70
71
|
phewsh style # Build your style identity
|
|
71
72
|
```
|
|
72
73
|
|
package/bin/phewsh.js
CHANGED
|
@@ -64,6 +64,7 @@ const COMMANDS = {
|
|
|
64
64
|
watch: () => require('../commands/watch')(),
|
|
65
65
|
mcp: () => require('../commands/mcp')(),
|
|
66
66
|
receipts: () => require('../commands/receipts')(),
|
|
67
|
+
update: () => require('../commands/update')(),
|
|
67
68
|
serve: () => require('../commands/serve')(),
|
|
68
69
|
sequence: () => require('../commands/sequence')(),
|
|
69
70
|
seq: () => require('../commands/sequence')(),
|
|
@@ -103,6 +104,7 @@ function showHelp() {
|
|
|
103
104
|
console.log(` ${b(w('configure'))}`);
|
|
104
105
|
console.log(` ${cyan('login')} ${g('Identity + API key + cloud sync')}`);
|
|
105
106
|
console.log(` ${cyan('link')} ${g('Link local .intent/ to cloud project')}`);
|
|
107
|
+
console.log(` ${cyan('update')} ${g('Update phewsh to the latest version')}`);
|
|
106
108
|
console.log('');
|
|
107
109
|
console.log(` ${g('Works in: Claude Code · Cursor · ChatGPT · any MCP agent')}`);
|
|
108
110
|
console.log(` ${g('No account needed. Account adds sync + sharing.')}`);
|
|
@@ -127,7 +129,7 @@ function checkForUpdates() {
|
|
|
127
129
|
newer[2] > current[2];
|
|
128
130
|
if (isNewer) {
|
|
129
131
|
console.log(g(`\n Update available: ${pkg.version} → ${data.version}`));
|
|
130
|
-
console.log(g(` Run:
|
|
132
|
+
console.log(g(` Run: phewsh update\n`));
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
})
|
package/commands/serve.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// phewsh serve — HTTP bridge server for live execution from the web UI
|
|
2
2
|
//
|
|
3
|
-
// Starts a local server that the PHEWSH web app connects to for live task
|
|
4
|
-
// When running, the web UI shows a green "Live" indicator and
|
|
3
|
+
// Starts a local server that the PHEWSH web app connects to for live task
|
|
4
|
+
// execution. When running, the web UI shows a green "Live" indicator and
|
|
5
|
+
// "Run Live" buttons for every agent CLI installed on this machine —
|
|
6
|
+
// Claude Code, Codex, Gemini, Cursor Agent, OpenCode. PHEWSH is not a
|
|
7
|
+
// harness; it dispatches to the harnesses you already have, and every run
|
|
8
|
+
// leaves a receipt (~/.phewsh/ — see `phewsh receipts`).
|
|
5
9
|
//
|
|
6
10
|
// Usage:
|
|
7
11
|
// phewsh serve Start on default port (7483)
|
|
@@ -31,15 +35,27 @@ function getPort() {
|
|
|
31
35
|
|
|
32
36
|
// ─── Runtime Detection ─────────────────────────────────────────────────────
|
|
33
37
|
|
|
38
|
+
// Harness runners — how to invoke each agent CLI headlessly. PHEWSH is not a
|
|
39
|
+
// harness; it's the layer that dispatches to whichever harnesses you have.
|
|
40
|
+
// Detection is honest: a runtime is only "connected" if its binary is on PATH.
|
|
41
|
+
const RUNNERS = {
|
|
42
|
+
'claude-code': { bin: 'claude', label: 'Claude Code', args: (p) => ['-p', p, '--output-format', 'text'] },
|
|
43
|
+
'codex': { bin: 'codex', label: 'Codex CLI', args: (p) => ['exec', p] },
|
|
44
|
+
'gemini': { bin: 'gemini', label: 'Gemini CLI', args: (p) => ['-p', p] },
|
|
45
|
+
'cursor': { bin: 'cursor-agent', label: 'Cursor Agent', args: (p) => ['-p', p, '--output-format', 'text'] },
|
|
46
|
+
'opencode': { bin: 'opencode', label: 'OpenCode', args: (p) => ['run', p] },
|
|
47
|
+
};
|
|
48
|
+
|
|
34
49
|
function detectRuntimes() {
|
|
35
50
|
const runtimes = [];
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
for (const [id, r] of Object.entries(RUNNERS)) {
|
|
53
|
+
let connected = false;
|
|
54
|
+
try {
|
|
55
|
+
execSync(`which ${r.bin}`, { stdio: 'pipe' });
|
|
56
|
+
connected = true;
|
|
57
|
+
} catch { /* not installed */ }
|
|
58
|
+
runtimes.push({ id, label: r.label, connected });
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
// Always report human as available
|
|
@@ -87,8 +103,9 @@ async function executeJob(jobId) {
|
|
|
87
103
|
|
|
88
104
|
const { runtimeId, packet } = job;
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
const runner = RUNNERS[runtimeId];
|
|
107
|
+
if (runner) {
|
|
108
|
+
await executeViaHarness(job, packet, runner);
|
|
92
109
|
} else {
|
|
93
110
|
job.status = 'error';
|
|
94
111
|
job.error = `Runtime ${runtimeId} not supported for live execution yet`;
|
|
@@ -96,7 +113,7 @@ async function executeJob(jobId) {
|
|
|
96
113
|
}
|
|
97
114
|
}
|
|
98
115
|
|
|
99
|
-
async function
|
|
116
|
+
async function executeViaHarness(job, packet, runner) {
|
|
100
117
|
// Build a prompt from the dispatch packet
|
|
101
118
|
const prompt = [
|
|
102
119
|
`# Task: ${packet.objective?.task || 'Execute task'}`,
|
|
@@ -115,15 +132,17 @@ async function executeViaClaude(job, packet) {
|
|
|
115
132
|
].filter(Boolean).join('\n');
|
|
116
133
|
|
|
117
134
|
return new Promise((resolve) => {
|
|
118
|
-
job.statusText =
|
|
135
|
+
job.statusText = `Launching ${runner.label}...`;
|
|
119
136
|
|
|
120
|
-
|
|
121
|
-
const child = spawn('claude', ['-p', prompt, '--output-format', 'text'], {
|
|
137
|
+
const child = spawn(runner.bin, runner.args(prompt), {
|
|
122
138
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
123
139
|
env: { ...process.env },
|
|
124
140
|
cwd: process.cwd(),
|
|
125
141
|
});
|
|
126
142
|
|
|
143
|
+
// Some harnesses (codex exec, gemini) wait for stdin EOF before running.
|
|
144
|
+
child.stdin.end();
|
|
145
|
+
|
|
127
146
|
let stdout = '';
|
|
128
147
|
let stderr = '';
|
|
129
148
|
|
|
@@ -149,7 +168,7 @@ async function executeViaClaude(job, packet) {
|
|
|
149
168
|
console.log(` ${green('✓')} Job ${job.jobId.slice(0, 8)} completed`);
|
|
150
169
|
} else {
|
|
151
170
|
job.status = 'error';
|
|
152
|
-
job.error = stderr.trim() ||
|
|
171
|
+
job.error = stderr.trim() || `${runner.label} exited with code ${code}`;
|
|
153
172
|
job.statusText = 'Failed';
|
|
154
173
|
console.log(` ${yellow('✗')} Job ${job.jobId.slice(0, 8)} failed: ${job.error.slice(0, 100)}`);
|
|
155
174
|
}
|
|
@@ -160,11 +179,11 @@ async function executeViaClaude(job, packet) {
|
|
|
160
179
|
taskId: packet?.id || job.actionId || job.jobId,
|
|
161
180
|
result: success ? job.result : job.error,
|
|
162
181
|
success,
|
|
163
|
-
agentId:
|
|
182
|
+
agentId: job.runtimeId,
|
|
164
183
|
executor: 'phewsh-serve',
|
|
165
184
|
reportedAt: new Date().toISOString(),
|
|
166
185
|
});
|
|
167
|
-
recordSessionEvent(
|
|
186
|
+
recordSessionEvent(job.runtimeId, 'web', 'task_complete', {
|
|
168
187
|
taskId: packet?.id || job.actionId || job.jobId,
|
|
169
188
|
success,
|
|
170
189
|
result: (success ? job.result : job.error || '').slice(0, 200),
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// phewsh update — update the CLI to the latest published version.
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// phewsh update Check npm and install the latest version
|
|
5
|
+
// phewsh update --check Just compare versions, don't install
|
|
6
|
+
|
|
7
|
+
const { spawn } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const b = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
10
|
+
const w = (s) => `\x1b[97m${s}\x1b[0m`;
|
|
11
|
+
const g = (s) => `\x1b[38;2;130;142;138m${s}\x1b[0m`;
|
|
12
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
13
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
14
|
+
|
|
15
|
+
function isNewer(latest, current) {
|
|
16
|
+
const l = latest.split('.').map(Number);
|
|
17
|
+
const c = current.split('.').map(Number);
|
|
18
|
+
return l[0] !== c[0] ? l[0] > c[0] : l[1] !== c[1] ? l[1] > c[1] : l[2] > c[2];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
const checkOnly = process.argv.includes('--check');
|
|
23
|
+
const pkg = require('../package.json');
|
|
24
|
+
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(` ${b(w('phewsh update'))}`);
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log(` ${g('current:')} v${pkg.version}`);
|
|
29
|
+
|
|
30
|
+
let latest;
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, {
|
|
33
|
+
signal: AbortSignal.timeout(5000),
|
|
34
|
+
});
|
|
35
|
+
latest = (await res.json()).version;
|
|
36
|
+
} catch {
|
|
37
|
+
console.log(` ${yellow('Could not reach the npm registry. Try again later.')}`);
|
|
38
|
+
console.log('');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
console.log(` ${g('latest: ')} v${latest}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
if (!latest || !isNewer(latest, pkg.version)) {
|
|
45
|
+
console.log(` ${green('Already up to date.')}`);
|
|
46
|
+
console.log('');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (checkOnly) {
|
|
51
|
+
console.log(` ${w(`v${latest} available.`)} Run ${w('phewsh update')} to install.`);
|
|
52
|
+
console.log('');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(` ${g('Installing')} ${w(`phewsh@${latest}`)} ${g('via npm…')}`);
|
|
57
|
+
console.log('');
|
|
58
|
+
|
|
59
|
+
const child = spawn('npm', ['install', '-g', `${pkg.name}@latest`], { stdio: 'inherit' });
|
|
60
|
+
child.on('close', (code) => {
|
|
61
|
+
console.log('');
|
|
62
|
+
if (code === 0) {
|
|
63
|
+
console.log(` ${green('✓')} Updated to v${latest}. New shells pick it up immediately.`);
|
|
64
|
+
} else {
|
|
65
|
+
console.log(` ${yellow('Update failed')} (npm exited ${code}).`);
|
|
66
|
+
console.log(` ${g('If it was a permissions error, fix your npm prefix or run:')}`);
|
|
67
|
+
console.log(` ${w(`sudo npm install -g ${pkg.name}@latest`)}`);
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
process.exit(code ?? 0);
|
|
71
|
+
});
|
|
72
|
+
child.on('error', (err) => {
|
|
73
|
+
console.log(` ${yellow('Could not run npm:')} ${err.message}`);
|
|
74
|
+
console.log('');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = main;
|