orquesta-agent 0.2.240 → 0.2.242
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/dist/executor.d.ts +42 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +168 -59
- package/dist/executor.js.map +1 -1
- package/dist/index.js +147 -44
- package/dist/index.js.map +1 -1
- package/dist/local-server.d.ts +1 -1
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +7 -4
- package/dist/local-server.js.map +1 -1
- package/dist/socketio-transport.d.ts +2 -0
- package/dist/socketio-transport.d.ts.map +1 -1
- package/dist/socketio-transport.js.map +1 -1
- package/dist/supabase.d.ts +17 -0
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js.map +1 -1
- package/dist/ui/public/welcome.js +8 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -102,7 +102,8 @@ async function setup() {
|
|
|
102
102
|
console.log(' orquesta-agent ui --open');
|
|
103
103
|
console.log('');
|
|
104
104
|
console.log(' Then add your agents from the browser interface');
|
|
105
|
-
console.log(' URL: http://localhost:8080');
|
|
105
|
+
console.log(' URL: http://localhost:8080 (if 8080 is taken the manager picks a');
|
|
106
|
+
console.log(' free port and prints the real URL on startup)');
|
|
106
107
|
console.log('');
|
|
107
108
|
console.log('Or connect directly from command line:');
|
|
108
109
|
console.log(' orquesta-agent --token <your-token> --daemon');
|
|
@@ -141,6 +142,104 @@ async function refreshInjectedCredentials() {
|
|
|
141
142
|
// Non-fatal: keep the previously injected credentials.
|
|
142
143
|
}
|
|
143
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Commit assisted work and hand the server the raw facts: commit sha, branch,
|
|
147
|
+
* and `git diff --numstat` text.
|
|
148
|
+
*
|
|
149
|
+
* Shared by the TWO ways an assisted item gets worked — a dispatched prompt
|
|
150
|
+
* (the agent drove the CLI) and a claimed interactive session (a person sat at
|
|
151
|
+
* the terminal). Both finish identically: the server evaluates the gate and
|
|
152
|
+
* parks the item at `needs_review`. The split is the point — a gate computed on
|
|
153
|
+
* the machine being gated protects nobody, and this machine is the customer's.
|
|
154
|
+
*
|
|
155
|
+
* Best-effort throughout: a failure here must not fail work that already
|
|
156
|
+
* happened. The reviewer sees the item stuck in_progress and can nudge the run.
|
|
157
|
+
*/
|
|
158
|
+
async function postAssistedReport(opts) {
|
|
159
|
+
try {
|
|
160
|
+
const commit = await gitCommitAndPush(opts.execDir, opts.commitMessage);
|
|
161
|
+
// With an explicit base we always diff, even when this call committed
|
|
162
|
+
// nothing — a person working interactively may well have committed by
|
|
163
|
+
// hand, and reporting an empty diff for real work would send the reviewer
|
|
164
|
+
// an item that looks like a no-op.
|
|
165
|
+
const base = opts.diffBase && opts.diffBase !== opts.branch
|
|
166
|
+
? opts.diffBase
|
|
167
|
+
: (commit.sha ? 'HEAD~1' : null);
|
|
168
|
+
const numstat = base ? gitNumstatSince(opts.execDir, base) : '';
|
|
169
|
+
let sha = commit.sha;
|
|
170
|
+
if (!sha && base) {
|
|
171
|
+
try {
|
|
172
|
+
sha = execSync('git rev-parse HEAD', { cwd: opts.execDir, stdio: 'pipe' }).toString().trim() || null;
|
|
173
|
+
}
|
|
174
|
+
catch { /* leave null — the server treats a missing sha as "no commit" */ }
|
|
175
|
+
}
|
|
176
|
+
const apiUrl = process.env.ORQUESTA_API_URL || 'https://getorquesta.com';
|
|
177
|
+
const res = await fetch(`${apiUrl}/api/agent/assisted-report`, {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: { 'Content-Type': 'application/json' },
|
|
180
|
+
body: JSON.stringify({
|
|
181
|
+
token: opts.token,
|
|
182
|
+
promptId: opts.promptId,
|
|
183
|
+
commitSha: sha,
|
|
184
|
+
branch: opts.branch,
|
|
185
|
+
numstat,
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
logger.warn(`[assisted] report rejected (${res.status}) for item ${opts.itemId}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
logger.success(`[assisted] reported ${sha ? sha.slice(0, 8) : 'no commit'} on ${opts.branch}`);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
logger.warn(`[assisted] report failed: ${err}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Prepare a claimed assisted item for an interactive session: put the checkout
|
|
200
|
+
* on the run's branch and remember where it started.
|
|
201
|
+
*
|
|
202
|
+
* Returns the envelope the executor carries for the life of the session, or
|
|
203
|
+
* undefined when this session is not part of an assisted run.
|
|
204
|
+
*/
|
|
205
|
+
function prepareAssistedSession(assisted, sessDir) {
|
|
206
|
+
if (!assisted?.branch)
|
|
207
|
+
return undefined;
|
|
208
|
+
if (!fs.existsSync(path.join(sessDir, '.git'))) {
|
|
209
|
+
logger.warn(`[assisted] ${sessDir} is not a git checkout — session runs without branch isolation`);
|
|
210
|
+
return assisted;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
execSync(`git checkout ${assisted.branch} 2>/dev/null || git checkout -b ${assisted.branch}`, { cwd: sessDir, stdio: 'pipe' });
|
|
214
|
+
logger.info(`[assisted] Checked out branch: ${assisted.branch}`);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
logger.warn(`[assisted] Branch checkout failed: ${err}`);
|
|
218
|
+
}
|
|
219
|
+
let baseSha;
|
|
220
|
+
try {
|
|
221
|
+
baseSha = execSync('git rev-parse HEAD', { cwd: sessDir, stdio: 'pipe' }).toString().trim() || undefined;
|
|
222
|
+
}
|
|
223
|
+
catch { /* a branch with no commits yet — HEAD~1 fallback covers it */ }
|
|
224
|
+
return { ...assisted, baseSha };
|
|
225
|
+
}
|
|
226
|
+
/** Report a finished interactive session that was working a claimed item. */
|
|
227
|
+
async function reportAssistedSession(info, token) {
|
|
228
|
+
const assisted = info.assisted;
|
|
229
|
+
if (!assisted)
|
|
230
|
+
return;
|
|
231
|
+
if (!fs.existsSync(path.join(info.workingDirectory, '.git')))
|
|
232
|
+
return;
|
|
233
|
+
await postAssistedReport({
|
|
234
|
+
token,
|
|
235
|
+
promptId: assisted.promptId,
|
|
236
|
+
itemId: assisted.itemId,
|
|
237
|
+
execDir: info.workingDirectory,
|
|
238
|
+
branch: assisted.branch,
|
|
239
|
+
diffBase: assisted.baseSha || null,
|
|
240
|
+
commitMessage: `assisted: interactive session for item ${assisted.itemId.slice(0, 8)}`,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
144
243
|
async function connect(options) {
|
|
145
244
|
logger.banner(VERSION);
|
|
146
245
|
// Single-instance guard for DIRECT runs (e.g. `npx -y orquesta-agent` with no
|
|
@@ -544,7 +643,9 @@ async function connect(options) {
|
|
|
544
643
|
executePromptSequentialOSS(next);
|
|
545
644
|
}
|
|
546
645
|
}
|
|
547
|
-
setOnSessionEnded(() => {
|
|
646
|
+
setOnSessionEnded((info) => {
|
|
647
|
+
if (info?.assisted && token)
|
|
648
|
+
void reportAssistedSession(info, token);
|
|
548
649
|
if (promptQueue.length > 0) {
|
|
549
650
|
logger.info(`Session ended — draining ${promptQueue.length} queued prompt(s)`);
|
|
550
651
|
drainQueueOSS();
|
|
@@ -600,6 +701,8 @@ async function connect(options) {
|
|
|
600
701
|
else
|
|
601
702
|
logger.warn(`session:start requested dir "${rawDir}" does not exist — falling back to ${workingDirectory}`);
|
|
602
703
|
}
|
|
704
|
+
// A claimed assisted item pins the session to the run's branch.
|
|
705
|
+
const assisted = prepareAssistedSession(payload.assisted, sessDir);
|
|
603
706
|
// Pick up credentials created/rotated since startup before spawning.
|
|
604
707
|
void refreshInjectedCredentials().finally(() => {
|
|
605
708
|
startSession({
|
|
@@ -611,6 +714,7 @@ async function connect(options) {
|
|
|
611
714
|
subAgentId: payload.subAgentId,
|
|
612
715
|
subAgentName: payload.subAgentName,
|
|
613
716
|
resume: payload.resume,
|
|
717
|
+
assisted,
|
|
614
718
|
});
|
|
615
719
|
});
|
|
616
720
|
},
|
|
@@ -691,47 +795,20 @@ async function connect(options) {
|
|
|
691
795
|
return promptQueue.some(q => q.promptId === promptId) ||
|
|
692
796
|
[...runningExecutions.values()].some(e => e.promptId === promptId);
|
|
693
797
|
}
|
|
694
|
-
/**
|
|
695
|
-
* Commit the assisted item's work and hand the server the raw facts.
|
|
696
|
-
*
|
|
697
|
-
* Facts only: the commit sha, the branch, and `git diff --numstat` text.
|
|
698
|
-
* The server evaluates the gate. That split is the point — a gate computed
|
|
699
|
-
* on the machine being gated protects nobody, and this machine belongs to
|
|
700
|
-
* the customer.
|
|
701
|
-
*
|
|
702
|
-
* Best-effort throughout: a failure here must not fail the prompt, which
|
|
703
|
-
* has already produced real work. The reviewer sees the item stuck
|
|
704
|
-
* in_progress and can nudge the run instead.
|
|
705
|
-
*/
|
|
798
|
+
/** Report a finished dispatched prompt. See `postAssistedReport`. */
|
|
706
799
|
async function reportAssistedResult(payload, execDir, baseBranch) {
|
|
707
800
|
const assisted = payload.assisted;
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
token,
|
|
720
|
-
promptId: payload.promptId,
|
|
721
|
-
commitSha: commit.sha,
|
|
722
|
-
branch: assisted.branch,
|
|
723
|
-
numstat,
|
|
724
|
-
}),
|
|
725
|
-
});
|
|
726
|
-
if (!res.ok) {
|
|
727
|
-
logger.warn(`[assisted] report rejected (${res.status}) for item ${assisted.itemId}`);
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
logger.success(`[assisted] reported ${commit.sha ? commit.sha.slice(0, 8) : 'no commit'} on ${assisted.branch}`);
|
|
731
|
-
}
|
|
732
|
-
catch (err) {
|
|
733
|
-
logger.warn(`[assisted] report failed: ${err}`);
|
|
734
|
-
}
|
|
801
|
+
if (!token)
|
|
802
|
+
return;
|
|
803
|
+
await postAssistedReport({
|
|
804
|
+
token,
|
|
805
|
+
promptId: payload.promptId,
|
|
806
|
+
itemId: assisted.itemId,
|
|
807
|
+
execDir,
|
|
808
|
+
branch: assisted.branch,
|
|
809
|
+
diffBase: baseBranch,
|
|
810
|
+
commitMessage: `assisted: ${payload.content.split('\n')[0].slice(0, 120)}`,
|
|
811
|
+
});
|
|
735
812
|
}
|
|
736
813
|
async function executePrompt(payload) {
|
|
737
814
|
const execId = payload.id;
|
|
@@ -853,8 +930,12 @@ async function connect(options) {
|
|
|
853
930
|
executePrompt(next); // Fire and forget — runs concurrently
|
|
854
931
|
}
|
|
855
932
|
}
|
|
856
|
-
// When an interactive session ends,
|
|
857
|
-
|
|
933
|
+
// When an interactive session ends, report it if it was a claimed assisted
|
|
934
|
+
// item (the person's diff goes to review like any agent-produced one), then
|
|
935
|
+
// drain any prompts that were queued during it.
|
|
936
|
+
setOnSessionEnded((info) => {
|
|
937
|
+
if (info?.assisted && token)
|
|
938
|
+
void reportAssistedSession(info, token);
|
|
858
939
|
if (promptQueue.length > 0) {
|
|
859
940
|
logger.info(`Session ended — draining ${promptQueue.length} queued prompt(s)`);
|
|
860
941
|
drainQueue();
|
|
@@ -1060,6 +1141,11 @@ async function connect(options) {
|
|
|
1060
1141
|
logger.warn(`[workstream:${subCfg.name}] Branch checkout failed: ${err}`);
|
|
1061
1142
|
}
|
|
1062
1143
|
}
|
|
1144
|
+
// A claimed assisted item pins the session to the run's branch. It runs
|
|
1145
|
+
// AFTER the workstream checkout above so an assisted claim wins — the
|
|
1146
|
+
// two are never combined in the UI, and the run's isolation is the
|
|
1147
|
+
// whole reason the branch exists.
|
|
1148
|
+
const assisted = prepareAssistedSession(payload.assisted, sessDir);
|
|
1063
1149
|
// Pick up credentials created/rotated since startup before spawning.
|
|
1064
1150
|
void refreshInjectedCredentials().finally(() => {
|
|
1065
1151
|
startSession({
|
|
@@ -1072,6 +1158,7 @@ async function connect(options) {
|
|
|
1072
1158
|
subAgentName: payload.subAgentName || subCfg?.name,
|
|
1073
1159
|
resume: payload.resume,
|
|
1074
1160
|
resumeSessionId: payload.resumeSessionId,
|
|
1161
|
+
assisted,
|
|
1075
1162
|
});
|
|
1076
1163
|
});
|
|
1077
1164
|
},
|
|
@@ -1215,7 +1302,7 @@ async function connect(options) {
|
|
|
1215
1302
|
logger.info('Agent is ready and waiting for prompts...');
|
|
1216
1303
|
if (channel && validation.channelName) {
|
|
1217
1304
|
setLocalServerContext(channel, validation.channelName);
|
|
1218
|
-
startLocalServer().catch(err => logger.warn(`local server failed to start: ${err?.message || err}`));
|
|
1305
|
+
startLocalServer(options.localPort).catch(err => logger.warn(`local server failed to start: ${err?.message || err}`));
|
|
1219
1306
|
}
|
|
1220
1307
|
// First-run only: open the guided welcome tour (or print its URL on a
|
|
1221
1308
|
// headless host). Wired to the live agent so the "Configure now" step
|
|
@@ -1545,6 +1632,7 @@ program
|
|
|
1545
1632
|
.option('--cli-preference <preference>', 'CLI preference: auto (prefer orquesta), orquesta, claude, kimi, or cursor (cursor-agent). Defaults to auto.')
|
|
1546
1633
|
.option('--mode <mode>', 'Alias for --cli-preference: auto, orquesta, claude, kimi or cursor')
|
|
1547
1634
|
.option('--renew', 'Rotate this agent token: mint a fresh oat_, revoke the current one, print the new token, and exit. Update your start command/script with it to reconnect.')
|
|
1635
|
+
.option('--port <port>', 'Port for the agent\'s local bridge (default 7790). NOT the manager UI port — that one is `orquesta-agent ui --port <port>`.')
|
|
1548
1636
|
.option('-D, --daemon', 'Run in daemon mode with automatic restart on exit')
|
|
1549
1637
|
.option('--max-restarts <number>', 'Maximum restart attempts in daemon mode (0=unlimited)', '0')
|
|
1550
1638
|
.action((options) => {
|
|
@@ -1571,6 +1659,20 @@ program
|
|
|
1571
1659
|
logger.error('Invalid CLI preference/mode. Use "auto", "orquesta", "claude", or "kimi".');
|
|
1572
1660
|
process.exit(1);
|
|
1573
1661
|
}
|
|
1662
|
+
// `--port` used to be an UNKNOWN option on this command, and commander
|
|
1663
|
+
// exits(1) on those: the second agent someone started with
|
|
1664
|
+
// `orquesta-agent --token … --port 8081` (after the manager reported 8080
|
|
1665
|
+
// busy) never ran at all, so its project sat grey in the dashboard forever
|
|
1666
|
+
// and the port looked like the culprit. QA-54.
|
|
1667
|
+
let localPort;
|
|
1668
|
+
if (options.port !== undefined) {
|
|
1669
|
+
localPort = parseInt(String(options.port), 10);
|
|
1670
|
+
if (!Number.isFinite(localPort) || localPort < 0 || localPort > 65535) {
|
|
1671
|
+
logger.error(`Invalid --port "${options.port}". Use a number between 0 and 65535.`);
|
|
1672
|
+
process.exit(1);
|
|
1673
|
+
}
|
|
1674
|
+
logger.info(`Local bridge port set to ${localPort}. (The web manager's port is separate: orquesta-agent ui --port <port>.)`);
|
|
1675
|
+
}
|
|
1574
1676
|
// Check if daemon mode is requested (and we're not already a daemon child)
|
|
1575
1677
|
if (options.daemon && !isDaemonChild()) {
|
|
1576
1678
|
// Start in daemon mode - this will spawn the agent as a child process
|
|
@@ -1595,6 +1697,7 @@ program
|
|
|
1595
1697
|
sandboxMode: options.sandboxStrict === true ? 'strict' : undefined,
|
|
1596
1698
|
cliPreference: options.cliPreference,
|
|
1597
1699
|
agentName: options.name,
|
|
1700
|
+
localPort,
|
|
1598
1701
|
});
|
|
1599
1702
|
});
|
|
1600
1703
|
// Hook command — Claude Code / Cursor hook event handler
|