chatroom-cli 1.0.85 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -10
- package/dist/index.js +2085 -1336
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var __export = (target, all) => {
|
|
|
29
29
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
30
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
31
|
|
|
32
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
32
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/error.js
|
|
33
33
|
var require_error = __commonJS((exports) => {
|
|
34
34
|
class CommanderError extends Error {
|
|
35
35
|
constructor(exitCode, code, message) {
|
|
@@ -53,7 +53,7 @@ var require_error = __commonJS((exports) => {
|
|
|
53
53
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
56
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/argument.js
|
|
57
57
|
var require_argument = __commonJS((exports) => {
|
|
58
58
|
var { InvalidArgumentError } = require_error();
|
|
59
59
|
|
|
@@ -133,7 +133,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
133
133
|
exports.humanReadableArgName = humanReadableArgName;
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
136
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/help.js
|
|
137
137
|
var require_help = __commonJS((exports) => {
|
|
138
138
|
var { humanReadableArgName } = require_argument();
|
|
139
139
|
|
|
@@ -490,7 +490,7 @@ ${itemIndentStr}`);
|
|
|
490
490
|
exports.stripColor = stripColor;
|
|
491
491
|
});
|
|
492
492
|
|
|
493
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
493
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/option.js
|
|
494
494
|
var require_option = __commonJS((exports) => {
|
|
495
495
|
var { InvalidArgumentError } = require_error();
|
|
496
496
|
|
|
@@ -674,7 +674,7 @@ var require_option = __commonJS((exports) => {
|
|
|
674
674
|
exports.DualOptions = DualOptions;
|
|
675
675
|
});
|
|
676
676
|
|
|
677
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
677
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/suggestSimilar.js
|
|
678
678
|
var require_suggestSimilar = __commonJS((exports) => {
|
|
679
679
|
var maxDistance = 3;
|
|
680
680
|
function editDistance(a, b) {
|
|
@@ -747,7 +747,7 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
747
747
|
exports.suggestSimilar = suggestSimilar;
|
|
748
748
|
});
|
|
749
749
|
|
|
750
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
750
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/command.js
|
|
751
751
|
var require_command = __commonJS((exports) => {
|
|
752
752
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
753
753
|
var childProcess = __require("node:child_process");
|
|
@@ -2102,7 +2102,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2102
2102
|
exports.useColor = useColor;
|
|
2103
2103
|
});
|
|
2104
2104
|
|
|
2105
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
2105
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/index.js
|
|
2106
2106
|
var require_commander = __commonJS((exports) => {
|
|
2107
2107
|
var { Argument } = require_argument();
|
|
2108
2108
|
var { Command } = require_command();
|
|
@@ -9343,6 +9343,15 @@ var init_index_node = __esm(() => {
|
|
|
9343
9343
|
});
|
|
9344
9344
|
|
|
9345
9345
|
// src/infrastructure/convex/client.ts
|
|
9346
|
+
var exports_client = {};
|
|
9347
|
+
__export(exports_client, {
|
|
9348
|
+
resetConvexClient: () => resetConvexClient,
|
|
9349
|
+
isClientLoggingEnabled: () => isClientLoggingEnabled,
|
|
9350
|
+
getConvexWsClient: () => getConvexWsClient,
|
|
9351
|
+
getConvexUrl: () => getConvexUrl,
|
|
9352
|
+
getConvexClient: () => getConvexClient,
|
|
9353
|
+
CONVEX_URL: () => CONVEX_URL
|
|
9354
|
+
});
|
|
9346
9355
|
function getConvexUrl() {
|
|
9347
9356
|
return process.env.CHATROOM_CONVEX_URL || DEFAULT_CONVEX_URL;
|
|
9348
9357
|
}
|
|
@@ -9378,18 +9387,40 @@ async function getConvexWsClient() {
|
|
|
9378
9387
|
}
|
|
9379
9388
|
return wsClient;
|
|
9380
9389
|
}
|
|
9381
|
-
|
|
9390
|
+
function resetConvexClient() {
|
|
9391
|
+
client = null;
|
|
9392
|
+
if (wsClient) {
|
|
9393
|
+
wsClient.close();
|
|
9394
|
+
wsClient = null;
|
|
9395
|
+
}
|
|
9396
|
+
cachedUrl = null;
|
|
9397
|
+
}
|
|
9398
|
+
var DEFAULT_CONVEX_URL = "https://chatroom-cloud.duskfare.com", CONVEX_URL, client = null, wsClient = null, cachedUrl = null;
|
|
9382
9399
|
var init_client2 = __esm(() => {
|
|
9383
9400
|
init_index_node();
|
|
9401
|
+
CONVEX_URL = DEFAULT_CONVEX_URL;
|
|
9384
9402
|
});
|
|
9385
9403
|
|
|
9386
9404
|
// src/infrastructure/auth/storage.ts
|
|
9405
|
+
var exports_storage = {};
|
|
9406
|
+
__export(exports_storage, {
|
|
9407
|
+
saveAuthData: () => saveAuthData,
|
|
9408
|
+
loadAuthData: () => loadAuthData,
|
|
9409
|
+
isAuthenticated: () => isAuthenticated,
|
|
9410
|
+
getSessionId: () => getSessionId,
|
|
9411
|
+
getOtherSessionUrls: () => getOtherSessionUrls,
|
|
9412
|
+
getDeviceName: () => getDeviceName,
|
|
9413
|
+
getCliVersion: () => getVersion,
|
|
9414
|
+
getAuthFilePath: () => getAuthFilePath,
|
|
9415
|
+
getAllSessions: () => getAllSessions,
|
|
9416
|
+
clearAuthData: () => clearAuthData
|
|
9417
|
+
});
|
|
9387
9418
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, unlinkSync } from "node:fs";
|
|
9388
9419
|
import { homedir, hostname } from "node:os";
|
|
9389
9420
|
import { join as join2 } from "node:path";
|
|
9390
9421
|
function ensureConfigDir() {
|
|
9391
9422
|
if (!existsSync(CHATROOM_DIR)) {
|
|
9392
|
-
mkdirSync(CHATROOM_DIR, { recursive: true });
|
|
9423
|
+
mkdirSync(CHATROOM_DIR, { recursive: true, mode: 448 });
|
|
9393
9424
|
}
|
|
9394
9425
|
}
|
|
9395
9426
|
function getAuthFilePath() {
|
|
@@ -9466,7 +9497,7 @@ function saveAuthData(data) {
|
|
|
9466
9497
|
// To logout, run: chatroom auth logout
|
|
9467
9498
|
${JSON.stringify(multiEnvData, null, 2)}
|
|
9468
9499
|
`;
|
|
9469
|
-
writeFileSync(authPath, content, "utf-8");
|
|
9500
|
+
writeFileSync(authPath, content, { encoding: "utf-8", mode: 384 });
|
|
9470
9501
|
}
|
|
9471
9502
|
function clearAuthData() {
|
|
9472
9503
|
const authPath = getAuthFilePath();
|
|
@@ -9495,7 +9526,7 @@ function clearAuthData() {
|
|
|
9495
9526
|
// To logout, run: chatroom auth logout
|
|
9496
9527
|
${JSON.stringify(rawData, null, 2)}
|
|
9497
9528
|
`;
|
|
9498
|
-
writeFileSync(authPath, content, "utf-8");
|
|
9529
|
+
writeFileSync(authPath, content, { encoding: "utf-8", mode: 384 });
|
|
9499
9530
|
return true;
|
|
9500
9531
|
}
|
|
9501
9532
|
try {
|
|
@@ -9617,6 +9648,17 @@ var init_api3 = __esm(() => {
|
|
|
9617
9648
|
init_api2();
|
|
9618
9649
|
});
|
|
9619
9650
|
|
|
9651
|
+
// src/utils/terminal-safety.ts
|
|
9652
|
+
function sanitizeForTerminal(input) {
|
|
9653
|
+
return input.replace(/\u001B\][^\u0007]*(?:\u0007|\u001B\\)/g, "").replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, "");
|
|
9654
|
+
}
|
|
9655
|
+
function sanitizeUnknownForTerminal(value) {
|
|
9656
|
+
if (typeof value === "string") {
|
|
9657
|
+
return sanitizeForTerminal(value);
|
|
9658
|
+
}
|
|
9659
|
+
return sanitizeForTerminal(String(value));
|
|
9660
|
+
}
|
|
9661
|
+
|
|
9620
9662
|
// src/utils/error-formatting.ts
|
|
9621
9663
|
function formatError(message, suggestions) {
|
|
9622
9664
|
console.error(`❌ ${message}`);
|
|
@@ -9659,24 +9701,26 @@ function formatChatroomIdError(chatroomId) {
|
|
|
9659
9701
|
}
|
|
9660
9702
|
function isNetworkError(error) {
|
|
9661
9703
|
const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
9662
|
-
const code2 = error
|
|
9704
|
+
const code2 = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
|
|
9663
9705
|
return msg.includes("fetch failed") || msg.includes("failed to fetch") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("etimedout") || msg.includes("network") || msg.includes("connection refused") || msg.includes("socket hang up") || msg.includes("dns") || code2 === "ECONNREFUSED" || code2 === "ENOTFOUND" || code2 === "ETIMEDOUT" || code2 === "ECONNRESET";
|
|
9664
9706
|
}
|
|
9665
9707
|
function formatConnectivityError(error, backendUrl) {
|
|
9666
9708
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
9709
|
+
const safeBackendUrl = backendUrl ? sanitizeForTerminal(backendUrl) : undefined;
|
|
9667
9710
|
console.error(`
|
|
9668
|
-
❌ Could not reach the backend${
|
|
9669
|
-
console.error(` ${err.message}`);
|
|
9711
|
+
❌ Could not reach the backend${safeBackendUrl ? ` at ${safeBackendUrl}` : ""}`);
|
|
9712
|
+
console.error(` ${sanitizeForTerminal(err.message)}`);
|
|
9670
9713
|
console.error(`
|
|
9671
9714
|
Your session may still be valid. Please check:`);
|
|
9672
9715
|
console.error(` • Network connectivity`);
|
|
9673
9716
|
console.error(` • Whether the backend service is running`);
|
|
9674
|
-
if (
|
|
9675
|
-
console.error(` • CHATROOM_CONVEX_URL is correct (currently: ${
|
|
9717
|
+
if (safeBackendUrl) {
|
|
9718
|
+
console.error(` • CHATROOM_CONVEX_URL is correct (currently: ${safeBackendUrl})`);
|
|
9676
9719
|
}
|
|
9677
9720
|
console.error(`
|
|
9678
9721
|
Try again once the backend is reachable.`);
|
|
9679
9722
|
}
|
|
9723
|
+
var init_error_formatting = () => {};
|
|
9680
9724
|
|
|
9681
9725
|
// src/infrastructure/auth/middleware.ts
|
|
9682
9726
|
var exports_middleware = {};
|
|
@@ -9784,20 +9828,78 @@ async function checkAuth() {
|
|
|
9784
9828
|
var init_middleware = __esm(() => {
|
|
9785
9829
|
init_storage();
|
|
9786
9830
|
init_api3();
|
|
9831
|
+
init_error_formatting();
|
|
9787
9832
|
init_client2();
|
|
9788
9833
|
});
|
|
9789
9834
|
|
|
9790
|
-
// src/commands/auth-login.ts
|
|
9835
|
+
// src/commands/auth-login/index.ts
|
|
9791
9836
|
var exports_auth_login = {};
|
|
9792
9837
|
__export(exports_auth_login, {
|
|
9838
|
+
getWebAppUrl: () => getWebAppUrl,
|
|
9839
|
+
createDefaultDeps: () => createDefaultDeps,
|
|
9793
9840
|
authLogin: () => authLogin
|
|
9794
9841
|
});
|
|
9795
|
-
function
|
|
9842
|
+
async function openBrowser(url) {
|
|
9843
|
+
const { spawn } = await import("node:child_process");
|
|
9844
|
+
const platform = process.platform;
|
|
9845
|
+
try {
|
|
9846
|
+
if (platform === "darwin") {
|
|
9847
|
+
const child = spawn("open", [url], { detached: true, stdio: "ignore" });
|
|
9848
|
+
child.unref();
|
|
9849
|
+
} else if (platform === "win32") {
|
|
9850
|
+
const child = spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" });
|
|
9851
|
+
child.unref();
|
|
9852
|
+
} else {
|
|
9853
|
+
const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
|
|
9854
|
+
child.unref();
|
|
9855
|
+
}
|
|
9856
|
+
} catch {
|
|
9857
|
+
console.log(`
|
|
9858
|
+
⚠️ Could not open browser automatically.`);
|
|
9859
|
+
console.log(` Please visit the URL manually.`);
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
function createDefaultDeps() {
|
|
9863
|
+
return {
|
|
9864
|
+
backend: {
|
|
9865
|
+
mutation: async (endpoint, args) => {
|
|
9866
|
+
const client2 = await getConvexClient();
|
|
9867
|
+
return client2.mutation(endpoint, args);
|
|
9868
|
+
},
|
|
9869
|
+
query: async (endpoint, args) => {
|
|
9870
|
+
const client2 = await getConvexClient();
|
|
9871
|
+
return client2.query(endpoint, args);
|
|
9872
|
+
}
|
|
9873
|
+
},
|
|
9874
|
+
auth: {
|
|
9875
|
+
isAuthenticated,
|
|
9876
|
+
getAuthFilePath,
|
|
9877
|
+
saveAuthData,
|
|
9878
|
+
getDeviceName,
|
|
9879
|
+
getCliVersion: getVersion,
|
|
9880
|
+
getSessionId
|
|
9881
|
+
},
|
|
9882
|
+
browser: {
|
|
9883
|
+
open: openBrowser
|
|
9884
|
+
},
|
|
9885
|
+
clock: {
|
|
9886
|
+
now: () => Date.now(),
|
|
9887
|
+
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
9888
|
+
},
|
|
9889
|
+
process: {
|
|
9890
|
+
env: process.env,
|
|
9891
|
+
platform: process.platform,
|
|
9892
|
+
exit: (code2) => process.exit(code2),
|
|
9893
|
+
stdoutWrite: (text) => process.stdout.write(text)
|
|
9894
|
+
}
|
|
9895
|
+
};
|
|
9896
|
+
}
|
|
9897
|
+
function getWebAppUrl(d) {
|
|
9796
9898
|
const convexUrl = getConvexUrl();
|
|
9797
9899
|
if (convexUrl === PRODUCTION_CONVEX_URL2) {
|
|
9798
9900
|
return PRODUCTION_WEBAPP_URL;
|
|
9799
9901
|
}
|
|
9800
|
-
const webAppUrlOverride = process.env.CHATROOM_WEB_URL;
|
|
9902
|
+
const webAppUrlOverride = d.process.env.CHATROOM_WEB_URL;
|
|
9801
9903
|
if (webAppUrlOverride) {
|
|
9802
9904
|
return webAppUrlOverride;
|
|
9803
9905
|
}
|
|
@@ -9818,38 +9920,42 @@ To authenticate with a local/dev backend, you must also set`);
|
|
|
9818
9920
|
console.error(`
|
|
9819
9921
|
${"═".repeat(50)}
|
|
9820
9922
|
`);
|
|
9821
|
-
process.exit(1);
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
const
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
}
|
|
9836
|
-
} catch {
|
|
9837
|
-
console.log(`
|
|
9838
|
-
⚠️ Could not open browser automatically.`);
|
|
9839
|
-
console.log(` Please visit the URL manually.`);
|
|
9840
|
-
}
|
|
9841
|
-
}
|
|
9842
|
-
async function authLogin(options) {
|
|
9843
|
-
if (isAuthenticated() && !options.force) {
|
|
9844
|
-
console.log(`✅ Already authenticated.`);
|
|
9845
|
-
console.log(` Auth file: ${getAuthFilePath()}`);
|
|
9846
|
-
console.log(`
|
|
9923
|
+
d.process.exit(1);
|
|
9924
|
+
return "";
|
|
9925
|
+
}
|
|
9926
|
+
async function authLogin(options, deps) {
|
|
9927
|
+
const d = deps ?? createDefaultDeps();
|
|
9928
|
+
if (d.auth.isAuthenticated() && !options.force) {
|
|
9929
|
+
const sessionId = d.auth.getSessionId();
|
|
9930
|
+
if (sessionId) {
|
|
9931
|
+
try {
|
|
9932
|
+
const validation = await d.backend.query(api.cliAuth.validateSession, { sessionId });
|
|
9933
|
+
if (validation.valid) {
|
|
9934
|
+
console.log(`✅ Already authenticated.`);
|
|
9935
|
+
console.log(` Auth file: ${d.auth.getAuthFilePath()}`);
|
|
9936
|
+
console.log(`
|
|
9847
9937
|
Use --force to re-authenticate.`);
|
|
9848
|
-
|
|
9938
|
+
return;
|
|
9939
|
+
}
|
|
9940
|
+
const reason = validation.reason;
|
|
9941
|
+
if (reason === "Session expired") {
|
|
9942
|
+
console.log(`
|
|
9943
|
+
⚠️ Your session has expired. Re-authenticating automatically...`);
|
|
9944
|
+
} else {
|
|
9945
|
+
console.log(`
|
|
9946
|
+
⚠️ Session is no longer valid (${reason}). Re-authenticating...`);
|
|
9947
|
+
}
|
|
9948
|
+
} catch {
|
|
9949
|
+
console.log(`✅ Already authenticated (could not verify with backend).`);
|
|
9950
|
+
console.log(` Auth file: ${d.auth.getAuthFilePath()}`);
|
|
9951
|
+
console.log(`
|
|
9952
|
+
Use --force to re-authenticate.`);
|
|
9953
|
+
return;
|
|
9954
|
+
}
|
|
9955
|
+
}
|
|
9849
9956
|
}
|
|
9850
|
-
const
|
|
9851
|
-
const
|
|
9852
|
-
const cliVersion = getVersion();
|
|
9957
|
+
const deviceName = d.auth.getDeviceName();
|
|
9958
|
+
const cliVersion = d.auth.getCliVersion();
|
|
9853
9959
|
const convexUrl = getConvexUrl();
|
|
9854
9960
|
console.log(`
|
|
9855
9961
|
${"═".repeat(50)}`);
|
|
@@ -9865,17 +9971,17 @@ Device: ${deviceName}`);
|
|
|
9865
9971
|
}
|
|
9866
9972
|
console.log(`
|
|
9867
9973
|
⏳ Creating authentication request...`);
|
|
9868
|
-
const result = await
|
|
9974
|
+
const result = await d.backend.mutation(api.cliAuth.createAuthRequest, {
|
|
9869
9975
|
deviceName,
|
|
9870
9976
|
cliVersion
|
|
9871
9977
|
});
|
|
9872
9978
|
const { requestId, expiresAt } = result;
|
|
9873
|
-
const expiresInSeconds = Math.round((expiresAt -
|
|
9979
|
+
const expiresInSeconds = Math.round((expiresAt - d.clock.now()) / 1000);
|
|
9874
9980
|
console.log(`
|
|
9875
9981
|
✅ Auth request created`);
|
|
9876
9982
|
console.log(` Request ID: ${requestId.substring(0, 8)}...`);
|
|
9877
9983
|
console.log(` Expires in: ${expiresInSeconds} seconds`);
|
|
9878
|
-
const webAppUrl = getWebAppUrl();
|
|
9984
|
+
const webAppUrl = getWebAppUrl(d);
|
|
9879
9985
|
const authUrl = `${webAppUrl}/cli-auth?request=${requestId}`;
|
|
9880
9986
|
console.log(`
|
|
9881
9987
|
${"─".repeat(50)}`);
|
|
@@ -9889,21 +9995,21 @@ If the browser doesn't open, visit this URL:`);
|
|
|
9889
9995
|
${authUrl}`);
|
|
9890
9996
|
console.log(`
|
|
9891
9997
|
${"─".repeat(50)}`);
|
|
9892
|
-
await
|
|
9998
|
+
await d.browser.open(authUrl);
|
|
9893
9999
|
console.log(`
|
|
9894
10000
|
⏳ Waiting for authorization...`);
|
|
9895
10001
|
console.log(` (Press Ctrl+C to cancel)
|
|
9896
10002
|
`);
|
|
9897
10003
|
let pollCount = 0;
|
|
9898
|
-
const maxPolls = Math.ceil((expiresAt -
|
|
10004
|
+
const maxPolls = Math.ceil((expiresAt - d.clock.now()) / AUTH_POLL_INTERVAL_MS);
|
|
9899
10005
|
const poll = async () => {
|
|
9900
10006
|
pollCount++;
|
|
9901
10007
|
try {
|
|
9902
|
-
const status = await
|
|
10008
|
+
const status = await d.backend.query(api.cliAuth.getAuthRequestStatus, {
|
|
9903
10009
|
requestId
|
|
9904
10010
|
});
|
|
9905
10011
|
if (status.status === "approved" && status.sessionId) {
|
|
9906
|
-
saveAuthData({
|
|
10012
|
+
d.auth.saveAuthData({
|
|
9907
10013
|
sessionId: status.sessionId,
|
|
9908
10014
|
createdAt: new Date().toISOString(),
|
|
9909
10015
|
deviceName,
|
|
@@ -9914,45 +10020,48 @@ ${"═".repeat(50)}`);
|
|
|
9914
10020
|
console.log(`✅ AUTHENTICATION SUCCESSFUL`);
|
|
9915
10021
|
console.log(`${"═".repeat(50)}`);
|
|
9916
10022
|
console.log(`
|
|
9917
|
-
Session stored at: ${getAuthFilePath()}`);
|
|
10023
|
+
Session stored at: ${d.auth.getAuthFilePath()}`);
|
|
9918
10024
|
console.log(`
|
|
9919
10025
|
You can now use chatroom commands.`);
|
|
9920
|
-
return
|
|
10026
|
+
return "done";
|
|
9921
10027
|
}
|
|
9922
10028
|
if (status.status === "denied") {
|
|
9923
10029
|
console.log(`
|
|
9924
10030
|
❌ Authorization denied by user.`);
|
|
9925
|
-
process.exit(1);
|
|
10031
|
+
d.process.exit(1);
|
|
10032
|
+
return "exit";
|
|
9926
10033
|
}
|
|
9927
10034
|
if (status.status === "expired" || status.status === "not_found") {
|
|
9928
10035
|
console.log(`
|
|
9929
10036
|
❌ Authorization request expired.`);
|
|
9930
10037
|
console.log(` Please try again: chatroom auth login`);
|
|
9931
|
-
process.exit(1);
|
|
10038
|
+
d.process.exit(1);
|
|
10039
|
+
return "exit";
|
|
9932
10040
|
}
|
|
9933
10041
|
if (pollCount % 5 === 0) {
|
|
9934
|
-
const remainingSeconds = Math.round((expiresAt -
|
|
9935
|
-
process.
|
|
10042
|
+
const remainingSeconds = Math.round((expiresAt - d.clock.now()) / 1000);
|
|
10043
|
+
d.process.stdoutWrite(`\r Waiting... (${remainingSeconds}s remaining) `);
|
|
9936
10044
|
}
|
|
9937
10045
|
if (pollCount >= maxPolls) {
|
|
9938
10046
|
console.log(`
|
|
9939
10047
|
❌ Authorization request expired.`);
|
|
9940
10048
|
console.log(` Please try again: chatroom auth login`);
|
|
9941
|
-
process.exit(1);
|
|
10049
|
+
d.process.exit(1);
|
|
10050
|
+
return "exit";
|
|
9942
10051
|
}
|
|
9943
|
-
return
|
|
10052
|
+
return "continue";
|
|
9944
10053
|
} catch (error) {
|
|
9945
10054
|
const err = error;
|
|
9946
10055
|
console.error(`
|
|
9947
10056
|
⚠️ Error polling for authorization: ${err.message}`);
|
|
9948
|
-
return
|
|
10057
|
+
return "continue";
|
|
9949
10058
|
}
|
|
9950
10059
|
};
|
|
9951
10060
|
while (true) {
|
|
9952
|
-
const
|
|
9953
|
-
if (
|
|
10061
|
+
const result2 = await poll();
|
|
10062
|
+
if (result2 === "done" || result2 === "exit")
|
|
9954
10063
|
break;
|
|
9955
|
-
await
|
|
10064
|
+
await d.clock.delay(AUTH_POLL_INTERVAL_MS);
|
|
9956
10065
|
}
|
|
9957
10066
|
}
|
|
9958
10067
|
var AUTH_POLL_INTERVAL_MS = 2000, PRODUCTION_CONVEX_URL2 = "https://chatroom-cloud.duskfare.com", PRODUCTION_WEBAPP_URL = "https://chatroom.duskfare.com";
|
|
@@ -9962,23 +10071,34 @@ var init_auth_login = __esm(() => {
|
|
|
9962
10071
|
init_client2();
|
|
9963
10072
|
});
|
|
9964
10073
|
|
|
9965
|
-
// src/commands/auth-logout.ts
|
|
10074
|
+
// src/commands/auth-logout/index.ts
|
|
9966
10075
|
var exports_auth_logout = {};
|
|
9967
10076
|
__export(exports_auth_logout, {
|
|
9968
10077
|
authLogout: () => authLogout
|
|
9969
10078
|
});
|
|
9970
|
-
|
|
9971
|
-
|
|
10079
|
+
function createDefaultDeps2() {
|
|
10080
|
+
return {
|
|
10081
|
+
session: {
|
|
10082
|
+
isAuthenticated,
|
|
10083
|
+
clearAuthData,
|
|
10084
|
+
getAuthFilePath
|
|
10085
|
+
}
|
|
10086
|
+
};
|
|
10087
|
+
}
|
|
10088
|
+
async function authLogout(deps) {
|
|
10089
|
+
const d = deps ?? createDefaultDeps2();
|
|
10090
|
+
if (!d.session.isAuthenticated()) {
|
|
9972
10091
|
console.log(`ℹ️ Not currently authenticated.`);
|
|
9973
10092
|
return;
|
|
9974
10093
|
}
|
|
9975
|
-
const cleared = clearAuthData();
|
|
10094
|
+
const cleared = d.session.clearAuthData();
|
|
9976
10095
|
if (cleared) {
|
|
9977
10096
|
console.log(`✅ Logged out successfully.`);
|
|
9978
|
-
console.log(` Removed: ${getAuthFilePath()}`);
|
|
10097
|
+
console.log(` Removed: ${d.session.getAuthFilePath()}`);
|
|
9979
10098
|
} else {
|
|
9980
10099
|
console.error(`❌ Failed to clear authentication data.`);
|
|
9981
10100
|
process.exit(1);
|
|
10101
|
+
return;
|
|
9982
10102
|
}
|
|
9983
10103
|
}
|
|
9984
10104
|
var init_auth_logout = __esm(() => {
|
|
@@ -10117,8 +10237,7 @@ function createNewEndpointConfig() {
|
|
|
10117
10237
|
registeredAt: now,
|
|
10118
10238
|
lastSyncedAt: now,
|
|
10119
10239
|
availableHarnesses,
|
|
10120
|
-
harnessVersions: detectHarnessVersions(availableHarnesses)
|
|
10121
|
-
chatroomAgents: {}
|
|
10240
|
+
harnessVersions: detectHarnessVersions(availableHarnesses)
|
|
10122
10241
|
};
|
|
10123
10242
|
}
|
|
10124
10243
|
function ensureMachineRegistered() {
|
|
@@ -10145,29 +10264,6 @@ function getMachineId() {
|
|
|
10145
10264
|
const config = loadMachineConfig();
|
|
10146
10265
|
return config?.machineId ?? null;
|
|
10147
10266
|
}
|
|
10148
|
-
function updateAgentContext(chatroomId, role, agentType, workingDir) {
|
|
10149
|
-
const config = loadMachineConfig();
|
|
10150
|
-
if (!config) {
|
|
10151
|
-
throw new Error("Machine not registered. Run ensureMachineRegistered() first.");
|
|
10152
|
-
}
|
|
10153
|
-
const now = new Date().toISOString();
|
|
10154
|
-
if (!config.chatroomAgents[chatroomId]) {
|
|
10155
|
-
config.chatroomAgents[chatroomId] = {};
|
|
10156
|
-
}
|
|
10157
|
-
config.chatroomAgents[chatroomId][role] = {
|
|
10158
|
-
agentType,
|
|
10159
|
-
workingDir,
|
|
10160
|
-
lastStartedAt: now
|
|
10161
|
-
};
|
|
10162
|
-
saveMachineConfig(config);
|
|
10163
|
-
}
|
|
10164
|
-
function getAgentContext(chatroomId, role) {
|
|
10165
|
-
const config = loadMachineConfig();
|
|
10166
|
-
if (!config) {
|
|
10167
|
-
return null;
|
|
10168
|
-
}
|
|
10169
|
-
return config.chatroomAgents[chatroomId]?.[role] ?? null;
|
|
10170
|
-
}
|
|
10171
10267
|
var CHATROOM_DIR2, MACHINE_FILE = "machine.json";
|
|
10172
10268
|
var init_storage2 = __esm(() => {
|
|
10173
10269
|
init_detection();
|
|
@@ -10261,248 +10357,236 @@ var init_daemon_state = __esm(() => {
|
|
|
10261
10357
|
STATE_DIR = join4(CHATROOM_DIR3, "machines", "state");
|
|
10262
10358
|
});
|
|
10263
10359
|
|
|
10360
|
+
// src/infrastructure/machine/intentional-stops.ts
|
|
10361
|
+
function agentKey2(chatroomId, role) {
|
|
10362
|
+
return `${chatroomId}:${role.toLowerCase()}`;
|
|
10363
|
+
}
|
|
10364
|
+
function markIntentionalStop(chatroomId, role) {
|
|
10365
|
+
intentionalStops.add(agentKey2(chatroomId, role));
|
|
10366
|
+
}
|
|
10367
|
+
function consumeIntentionalStop(chatroomId, role) {
|
|
10368
|
+
const key = agentKey2(chatroomId, role);
|
|
10369
|
+
if (intentionalStops.has(key)) {
|
|
10370
|
+
intentionalStops.delete(key);
|
|
10371
|
+
return true;
|
|
10372
|
+
}
|
|
10373
|
+
return false;
|
|
10374
|
+
}
|
|
10375
|
+
function clearIntentionalStop(chatroomId, role) {
|
|
10376
|
+
intentionalStops.delete(agentKey2(chatroomId, role));
|
|
10377
|
+
}
|
|
10378
|
+
var intentionalStops;
|
|
10379
|
+
var init_intentional_stops = __esm(() => {
|
|
10380
|
+
intentionalStops = new Set;
|
|
10381
|
+
});
|
|
10382
|
+
|
|
10264
10383
|
// src/infrastructure/machine/index.ts
|
|
10265
10384
|
var init_machine = __esm(() => {
|
|
10266
10385
|
init_types();
|
|
10267
10386
|
init_storage2();
|
|
10268
10387
|
init_daemon_state();
|
|
10388
|
+
init_intentional_stops();
|
|
10269
10389
|
});
|
|
10270
10390
|
|
|
10271
|
-
// src/infrastructure/
|
|
10272
|
-
import { spawn } from "node:child_process";
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
writeFileSync4(tempPath, prompt, { encoding: "utf-8", mode: 384 });
|
|
10280
|
-
return tempPath;
|
|
10281
|
-
}
|
|
10282
|
-
function scheduleCleanup(filePath, delayMs = 5000) {
|
|
10283
|
-
setTimeout(() => {
|
|
10284
|
-
try {
|
|
10285
|
-
unlinkSync2(filePath);
|
|
10286
|
-
} catch {}
|
|
10287
|
-
}, delayMs);
|
|
10288
|
-
}
|
|
10289
|
-
function buildCombinedPrompt(rolePrompt, initialMessage) {
|
|
10290
|
-
return `${rolePrompt}
|
|
10291
|
-
|
|
10292
|
-
${initialMessage}`;
|
|
10391
|
+
// src/infrastructure/services/remote-agents/opencode/opencode-agent-service.ts
|
|
10392
|
+
import { spawn, execSync as execSync2 } from "node:child_process";
|
|
10393
|
+
function defaultDeps() {
|
|
10394
|
+
return {
|
|
10395
|
+
execSync: execSync2,
|
|
10396
|
+
spawn,
|
|
10397
|
+
kill: (pid, signal) => process.kill(pid, signal)
|
|
10398
|
+
};
|
|
10293
10399
|
}
|
|
10294
10400
|
|
|
10295
|
-
class
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10401
|
+
class OpenCodeAgentService {
|
|
10402
|
+
deps;
|
|
10403
|
+
processes = new Map;
|
|
10404
|
+
constructor(deps) {
|
|
10405
|
+
this.deps = { ...defaultDeps(), ...deps };
|
|
10406
|
+
}
|
|
10407
|
+
isInstalled() {
|
|
10408
|
+
try {
|
|
10409
|
+
const checkCmd = process.platform === "win32" ? `where ${OPENCODE_COMMAND}` : `which ${OPENCODE_COMMAND}`;
|
|
10410
|
+
this.deps.execSync(checkCmd, { stdio: "ignore" });
|
|
10411
|
+
return true;
|
|
10412
|
+
} catch {
|
|
10413
|
+
return false;
|
|
10305
10414
|
}
|
|
10415
|
+
}
|
|
10416
|
+
getVersion() {
|
|
10306
10417
|
try {
|
|
10307
|
-
const
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
childProcess.stdin?.write(config.stdinPrompt);
|
|
10315
|
-
childProcess.stdin?.end();
|
|
10316
|
-
}
|
|
10317
|
-
config.afterSpawn?.(childProcess);
|
|
10318
|
-
childProcess.unref();
|
|
10319
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
10320
|
-
if (childProcess.killed || childProcess.exitCode !== null) {
|
|
10321
|
-
return {
|
|
10322
|
-
success: false,
|
|
10323
|
-
message: `Agent process exited immediately (exit code: ${childProcess.exitCode})`
|
|
10324
|
-
};
|
|
10325
|
-
}
|
|
10326
|
-
const handle = {
|
|
10327
|
-
harness: this.harness,
|
|
10328
|
-
type: "process",
|
|
10329
|
-
pid: childProcess.pid,
|
|
10330
|
-
workingDir: options.workingDir
|
|
10331
|
-
};
|
|
10332
|
-
return {
|
|
10333
|
-
success: true,
|
|
10334
|
-
message: "Agent spawned successfully",
|
|
10335
|
-
handle,
|
|
10336
|
-
onExit: (callback) => {
|
|
10337
|
-
childProcess.on("exit", (code2, signal) => {
|
|
10338
|
-
callback(code2, signal);
|
|
10339
|
-
});
|
|
10340
|
-
}
|
|
10341
|
-
};
|
|
10342
|
-
} catch (error) {
|
|
10418
|
+
const output = this.deps.execSync(`${OPENCODE_COMMAND} --version`, {
|
|
10419
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10420
|
+
timeout: 5000
|
|
10421
|
+
}).toString().trim();
|
|
10422
|
+
const match = output.match(/v?(\d+)\.(\d+)\.(\d+)/);
|
|
10423
|
+
if (!match)
|
|
10424
|
+
return null;
|
|
10343
10425
|
return {
|
|
10344
|
-
|
|
10345
|
-
|
|
10426
|
+
version: `${match[1]}.${match[2]}.${match[3]}`,
|
|
10427
|
+
major: parseInt(match[1], 10)
|
|
10346
10428
|
};
|
|
10429
|
+
} catch {
|
|
10430
|
+
return null;
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
async listModels() {
|
|
10434
|
+
try {
|
|
10435
|
+
const output = this.deps.execSync(`${OPENCODE_COMMAND} models`, {
|
|
10436
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10437
|
+
timeout: 1e4
|
|
10438
|
+
}).toString().trim();
|
|
10439
|
+
if (!output)
|
|
10440
|
+
return [];
|
|
10441
|
+
return output.split(`
|
|
10442
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
10443
|
+
} catch {
|
|
10444
|
+
return [];
|
|
10347
10445
|
}
|
|
10348
10446
|
}
|
|
10349
|
-
async
|
|
10350
|
-
|
|
10351
|
-
|
|
10447
|
+
async spawn(options) {
|
|
10448
|
+
const args = ["run"];
|
|
10449
|
+
if (options.model) {
|
|
10450
|
+
args.push("--model", options.model);
|
|
10451
|
+
}
|
|
10452
|
+
const childProcess = this.deps.spawn(OPENCODE_COMMAND, args, {
|
|
10453
|
+
cwd: options.workingDir,
|
|
10454
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10455
|
+
shell: false,
|
|
10456
|
+
detached: true
|
|
10457
|
+
});
|
|
10458
|
+
childProcess.stdin?.write(options.prompt);
|
|
10459
|
+
childProcess.stdin?.end();
|
|
10460
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
10461
|
+
if (childProcess.killed || childProcess.exitCode !== null) {
|
|
10462
|
+
throw new Error(`Agent process exited immediately (exit code: ${childProcess.exitCode})`);
|
|
10463
|
+
}
|
|
10464
|
+
if (!childProcess.pid) {
|
|
10465
|
+
throw new Error("Agent process started but has no PID");
|
|
10466
|
+
}
|
|
10467
|
+
const pid = childProcess.pid;
|
|
10468
|
+
const context = options.context;
|
|
10469
|
+
const entry = { context, lastOutputAt: Date.now() };
|
|
10470
|
+
this.processes.set(pid, entry);
|
|
10471
|
+
const outputCallbacks = [];
|
|
10472
|
+
if (childProcess.stdout) {
|
|
10473
|
+
childProcess.stdout.pipe(process.stdout, { end: false });
|
|
10474
|
+
childProcess.stdout.on("data", () => {
|
|
10475
|
+
entry.lastOutputAt = Date.now();
|
|
10476
|
+
for (const cb of outputCallbacks)
|
|
10477
|
+
cb();
|
|
10478
|
+
});
|
|
10479
|
+
}
|
|
10480
|
+
if (childProcess.stderr) {
|
|
10481
|
+
childProcess.stderr.pipe(process.stderr, { end: false });
|
|
10482
|
+
childProcess.stderr.on("data", () => {
|
|
10483
|
+
entry.lastOutputAt = Date.now();
|
|
10484
|
+
for (const cb of outputCallbacks)
|
|
10485
|
+
cb();
|
|
10486
|
+
});
|
|
10352
10487
|
}
|
|
10353
|
-
|
|
10488
|
+
return {
|
|
10489
|
+
pid,
|
|
10490
|
+
onExit: (cb) => {
|
|
10491
|
+
childProcess.on("exit", (code2, signal) => {
|
|
10492
|
+
this.processes.delete(pid);
|
|
10493
|
+
cb({ code: code2, signal, context });
|
|
10494
|
+
});
|
|
10495
|
+
},
|
|
10496
|
+
onOutput: (cb) => {
|
|
10497
|
+
outputCallbacks.push(cb);
|
|
10498
|
+
}
|
|
10499
|
+
};
|
|
10500
|
+
}
|
|
10501
|
+
async stop(pid) {
|
|
10354
10502
|
try {
|
|
10355
|
-
|
|
10503
|
+
this.deps.kill(-pid, "SIGTERM");
|
|
10356
10504
|
} catch {
|
|
10357
10505
|
return;
|
|
10358
10506
|
}
|
|
10359
|
-
const KILL_TIMEOUT_MS = 5000;
|
|
10360
|
-
const POLL_INTERVAL_MS = 200;
|
|
10361
10507
|
const deadline = Date.now() + KILL_TIMEOUT_MS;
|
|
10362
10508
|
while (Date.now() < deadline) {
|
|
10363
10509
|
try {
|
|
10364
|
-
|
|
10510
|
+
this.deps.kill(pid, 0);
|
|
10365
10511
|
} catch {
|
|
10366
10512
|
return;
|
|
10367
10513
|
}
|
|
10368
10514
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
10369
10515
|
}
|
|
10370
10516
|
try {
|
|
10371
|
-
|
|
10517
|
+
this.deps.kill(-pid, "SIGKILL");
|
|
10372
10518
|
} catch {}
|
|
10373
10519
|
}
|
|
10374
|
-
|
|
10375
|
-
if (handle.type !== "process" || !handle.pid) {
|
|
10376
|
-
return false;
|
|
10377
|
-
}
|
|
10520
|
+
isAlive(pid) {
|
|
10378
10521
|
try {
|
|
10379
|
-
|
|
10522
|
+
this.deps.kill(pid, 0);
|
|
10380
10523
|
return true;
|
|
10381
10524
|
} catch {
|
|
10382
10525
|
return false;
|
|
10383
10526
|
}
|
|
10384
10527
|
}
|
|
10385
|
-
|
|
10386
|
-
return []
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
}
|
|
10392
|
-
var init_process_driver = () => {};
|
|
10393
|
-
|
|
10394
|
-
// src/infrastructure/agent-drivers/opencode-process-driver.ts
|
|
10395
|
-
import { execSync as execSync2 } from "node:child_process";
|
|
10396
|
-
var OpenCodeProcessDriver;
|
|
10397
|
-
var init_opencode_process_driver = __esm(() => {
|
|
10398
|
-
init_process_driver();
|
|
10399
|
-
init_types();
|
|
10400
|
-
OpenCodeProcessDriver = class OpenCodeProcessDriver extends ProcessDriver {
|
|
10401
|
-
harness = "opencode";
|
|
10402
|
-
capabilities = {
|
|
10403
|
-
sessionPersistence: false,
|
|
10404
|
-
abort: false,
|
|
10405
|
-
modelSelection: true,
|
|
10406
|
-
compaction: false,
|
|
10407
|
-
eventStreaming: false,
|
|
10408
|
-
messageInjection: false,
|
|
10409
|
-
dynamicModelDiscovery: true
|
|
10410
|
-
};
|
|
10411
|
-
buildSpawnConfig(options) {
|
|
10412
|
-
const command = AGENT_HARNESS_COMMANDS[this.harness];
|
|
10413
|
-
const combinedPrompt = buildCombinedPrompt(options.rolePrompt, options.initialMessage);
|
|
10414
|
-
const args = ["run"];
|
|
10415
|
-
if (options.model) {
|
|
10416
|
-
args.push("--model", options.model);
|
|
10417
|
-
}
|
|
10418
|
-
return {
|
|
10419
|
-
command,
|
|
10420
|
-
args,
|
|
10421
|
-
stdio: ["pipe", "inherit", "inherit"],
|
|
10422
|
-
writePromptToStdin: true,
|
|
10423
|
-
stdinPrompt: combinedPrompt
|
|
10424
|
-
};
|
|
10425
|
-
}
|
|
10426
|
-
async listModels() {
|
|
10427
|
-
try {
|
|
10428
|
-
const output = execSync2("opencode models", {
|
|
10429
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
10430
|
-
timeout: 1e4
|
|
10431
|
-
}).toString().trim();
|
|
10432
|
-
if (!output)
|
|
10433
|
-
return [];
|
|
10434
|
-
return output.split(`
|
|
10435
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
10436
|
-
} catch {
|
|
10437
|
-
return [];
|
|
10438
|
-
}
|
|
10439
|
-
}
|
|
10440
|
-
};
|
|
10441
|
-
});
|
|
10442
|
-
|
|
10443
|
-
// src/infrastructure/agent-drivers/registry.ts
|
|
10444
|
-
class DefaultDriverRegistry {
|
|
10445
|
-
drivers;
|
|
10446
|
-
constructor(drivers) {
|
|
10447
|
-
this.drivers = new Map;
|
|
10448
|
-
for (const driver of drivers) {
|
|
10449
|
-
this.drivers.set(driver.harness, driver);
|
|
10450
|
-
}
|
|
10451
|
-
}
|
|
10452
|
-
get(harness) {
|
|
10453
|
-
const driver = this.drivers.get(harness);
|
|
10454
|
-
if (!driver) {
|
|
10455
|
-
throw new Error(`No driver registered for harness: ${harness}`);
|
|
10456
|
-
}
|
|
10457
|
-
return driver;
|
|
10458
|
-
}
|
|
10459
|
-
all() {
|
|
10460
|
-
return Array.from(this.drivers.values());
|
|
10461
|
-
}
|
|
10462
|
-
capabilities(harness) {
|
|
10463
|
-
return this.get(harness).capabilities;
|
|
10528
|
+
getTrackedProcesses() {
|
|
10529
|
+
return Array.from(this.processes.entries()).map(([pid, entry]) => ({
|
|
10530
|
+
pid,
|
|
10531
|
+
context: entry.context,
|
|
10532
|
+
lastOutputAt: entry.lastOutputAt
|
|
10533
|
+
}));
|
|
10464
10534
|
}
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
if (!registryInstance) {
|
|
10468
|
-
registryInstance = new DefaultDriverRegistry([new OpenCodeProcessDriver]);
|
|
10535
|
+
untrack(pid) {
|
|
10536
|
+
this.processes.delete(pid);
|
|
10469
10537
|
}
|
|
10470
|
-
return registryInstance;
|
|
10471
10538
|
}
|
|
10472
|
-
var
|
|
10473
|
-
var
|
|
10474
|
-
init_opencode_process_driver();
|
|
10475
|
-
});
|
|
10539
|
+
var OPENCODE_COMMAND = "opencode", KILL_TIMEOUT_MS = 5000, POLL_INTERVAL_MS = 200;
|
|
10540
|
+
var init_opencode_agent_service = () => {};
|
|
10476
10541
|
|
|
10477
|
-
// src/infrastructure/
|
|
10478
|
-
var
|
|
10479
|
-
__export(
|
|
10480
|
-
|
|
10481
|
-
scheduleCleanup: () => scheduleCleanup,
|
|
10482
|
-
getDriverRegistry: () => getDriverRegistry,
|
|
10483
|
-
buildCombinedPrompt: () => buildCombinedPrompt,
|
|
10484
|
-
ProcessDriver: () => ProcessDriver,
|
|
10485
|
-
OpenCodeProcessDriver: () => OpenCodeProcessDriver
|
|
10542
|
+
// src/infrastructure/services/remote-agents/opencode/index.ts
|
|
10543
|
+
var exports_opencode = {};
|
|
10544
|
+
__export(exports_opencode, {
|
|
10545
|
+
OpenCodeAgentService: () => OpenCodeAgentService
|
|
10486
10546
|
});
|
|
10487
|
-
var
|
|
10488
|
-
|
|
10489
|
-
init_process_driver();
|
|
10490
|
-
init_process_driver();
|
|
10491
|
-
init_opencode_process_driver();
|
|
10547
|
+
var init_opencode = __esm(() => {
|
|
10548
|
+
init_opencode_agent_service();
|
|
10492
10549
|
});
|
|
10493
10550
|
|
|
10494
|
-
// src/commands/auth-status.ts
|
|
10551
|
+
// src/commands/auth-status/index.ts
|
|
10495
10552
|
var exports_auth_status = {};
|
|
10496
10553
|
__export(exports_auth_status, {
|
|
10497
10554
|
authStatus: () => authStatus
|
|
10498
10555
|
});
|
|
10499
|
-
async function
|
|
10556
|
+
async function listAvailableModelsDefault() {
|
|
10557
|
+
try {
|
|
10558
|
+
const { OpenCodeAgentService: OpenCodeAgentService2 } = await Promise.resolve().then(() => (init_opencode(), exports_opencode));
|
|
10559
|
+
const agentService = new OpenCodeAgentService2;
|
|
10560
|
+
return await agentService.listModels();
|
|
10561
|
+
} catch {
|
|
10562
|
+
return [];
|
|
10563
|
+
}
|
|
10564
|
+
}
|
|
10565
|
+
async function createDefaultDeps3() {
|
|
10566
|
+
const client2 = await getConvexClient();
|
|
10567
|
+
return {
|
|
10568
|
+
backend: {
|
|
10569
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
10570
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
10571
|
+
},
|
|
10572
|
+
session: {
|
|
10573
|
+
loadAuthData,
|
|
10574
|
+
getAuthFilePath,
|
|
10575
|
+
isAuthenticated
|
|
10576
|
+
},
|
|
10577
|
+
getVersion,
|
|
10578
|
+
ensureMachineRegistered,
|
|
10579
|
+
listAvailableModels: listAvailableModelsDefault
|
|
10580
|
+
};
|
|
10581
|
+
}
|
|
10582
|
+
async function authStatus(deps) {
|
|
10583
|
+
const d = deps ?? await createDefaultDeps3();
|
|
10500
10584
|
console.log(`
|
|
10501
10585
|
${"═".repeat(50)}`);
|
|
10502
10586
|
console.log(`\uD83D\uDD10 AUTHENTICATION STATUS`);
|
|
10503
10587
|
console.log(`${"═".repeat(50)}`);
|
|
10504
|
-
const authData = loadAuthData();
|
|
10505
|
-
if (!isAuthenticated() || !authData) {
|
|
10588
|
+
const authData = d.session.loadAuthData();
|
|
10589
|
+
if (!d.session.isAuthenticated() || !authData) {
|
|
10506
10590
|
console.log(`
|
|
10507
10591
|
❌ Not authenticated`);
|
|
10508
10592
|
console.log(`
|
|
@@ -10510,17 +10594,16 @@ ${"═".repeat(50)}`);
|
|
|
10510
10594
|
return;
|
|
10511
10595
|
}
|
|
10512
10596
|
console.log(`
|
|
10513
|
-
\uD83D\uDCC1 Auth file: ${getAuthFilePath()}`);
|
|
10597
|
+
\uD83D\uDCC1 Auth file: ${d.session.getAuthFilePath()}`);
|
|
10514
10598
|
console.log(`\uD83D\uDCC5 Created: ${authData.createdAt}`);
|
|
10515
10599
|
if (authData.deviceName) {
|
|
10516
10600
|
console.log(`\uD83D\uDCBB Device: ${authData.deviceName}`);
|
|
10517
10601
|
}
|
|
10518
|
-
console.log(`\uD83D\uDCE6 CLI Version: ${getVersion()}`);
|
|
10602
|
+
console.log(`\uD83D\uDCE6 CLI Version: ${d.getVersion()}`);
|
|
10519
10603
|
console.log(`
|
|
10520
10604
|
⏳ Validating session...`);
|
|
10521
10605
|
try {
|
|
10522
|
-
const
|
|
10523
|
-
const validation = await client2.query(api.cliAuth.validateSession, {
|
|
10606
|
+
const validation = await d.backend.query(api.cliAuth.validateSession, {
|
|
10524
10607
|
sessionId: authData.sessionId
|
|
10525
10608
|
});
|
|
10526
10609
|
if (validation.valid) {
|
|
@@ -10530,19 +10613,9 @@ ${"═".repeat(50)}`);
|
|
|
10530
10613
|
console.log(`\uD83D\uDC64 User: ${validation.userName}`);
|
|
10531
10614
|
}
|
|
10532
10615
|
try {
|
|
10533
|
-
const machineInfo = ensureMachineRegistered();
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
const { getDriverRegistry: getDriverRegistry2 } = await Promise.resolve().then(() => (init_agent_drivers(), exports_agent_drivers));
|
|
10537
|
-
const registry = getDriverRegistry2();
|
|
10538
|
-
for (const driver of registry.all()) {
|
|
10539
|
-
if (driver.capabilities.dynamicModelDiscovery) {
|
|
10540
|
-
const models = await driver.listModels();
|
|
10541
|
-
availableModels = availableModels.concat(models);
|
|
10542
|
-
}
|
|
10543
|
-
}
|
|
10544
|
-
} catch {}
|
|
10545
|
-
await client2.mutation(api.machines.register, {
|
|
10616
|
+
const machineInfo = d.ensureMachineRegistered();
|
|
10617
|
+
const availableModels = await d.listAvailableModels();
|
|
10618
|
+
await d.backend.mutation(api.machines.register, {
|
|
10546
10619
|
sessionId: authData.sessionId,
|
|
10547
10620
|
machineId: machineInfo.machineId,
|
|
10548
10621
|
hostname: machineInfo.hostname,
|
|
@@ -10586,47 +10659,46 @@ var init_auth_status = __esm(() => {
|
|
|
10586
10659
|
init_version();
|
|
10587
10660
|
});
|
|
10588
10661
|
|
|
10589
|
-
// src/commands/update.ts
|
|
10662
|
+
// src/commands/update/index.ts
|
|
10590
10663
|
var exports_update = {};
|
|
10591
10664
|
__export(exports_update, {
|
|
10592
10665
|
update: () => update
|
|
10593
10666
|
});
|
|
10594
10667
|
import { exec } from "node:child_process";
|
|
10595
10668
|
import { promisify } from "node:util";
|
|
10596
|
-
function
|
|
10597
|
-
return
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
await execAsync("npm --version");
|
|
10602
|
-
return true;
|
|
10603
|
-
} catch {
|
|
10604
|
-
return false;
|
|
10605
|
-
}
|
|
10606
|
-
}
|
|
10607
|
-
async function getLatestVersion() {
|
|
10608
|
-
try {
|
|
10609
|
-
const { stdout } = await execAsync("npm view chatroom-cli version");
|
|
10610
|
-
return stdout.trim();
|
|
10611
|
-
} catch {
|
|
10612
|
-
return null;
|
|
10613
|
-
}
|
|
10669
|
+
function createDefaultDeps4() {
|
|
10670
|
+
return {
|
|
10671
|
+
getVersion,
|
|
10672
|
+
exec: (cmd) => execAsync(cmd).then((r) => ({ stdout: r.stdout ?? "", stderr: r.stderr }))
|
|
10673
|
+
};
|
|
10614
10674
|
}
|
|
10615
|
-
async function update() {
|
|
10675
|
+
async function update(deps) {
|
|
10676
|
+
const d = deps ?? createDefaultDeps4();
|
|
10677
|
+
const log = console.log.bind(console);
|
|
10616
10678
|
log(`
|
|
10617
10679
|
\uD83D\uDD04 Checking for updates...
|
|
10618
10680
|
`);
|
|
10619
|
-
|
|
10681
|
+
try {
|
|
10682
|
+
await d.exec("npm --version");
|
|
10683
|
+
} catch {
|
|
10620
10684
|
console.error("❌ npm is not available. Please install npm to update.");
|
|
10621
10685
|
process.exit(1);
|
|
10686
|
+
return;
|
|
10622
10687
|
}
|
|
10623
|
-
const currentVersion =
|
|
10688
|
+
const currentVersion = d.getVersion();
|
|
10624
10689
|
log(` Current version: ${currentVersion}`);
|
|
10625
|
-
|
|
10690
|
+
let latestVersion = null;
|
|
10691
|
+
try {
|
|
10692
|
+
const { stdout } = await d.exec("npm view chatroom-cli version");
|
|
10693
|
+
latestVersion = stdout.trim() || null;
|
|
10694
|
+
} catch {
|
|
10695
|
+
latestVersion = null;
|
|
10696
|
+
}
|
|
10626
10697
|
if (!latestVersion) {
|
|
10627
10698
|
console.error("❌ Could not check for latest version.");
|
|
10628
10699
|
console.error(" You can manually update with: npm install -g chatroom-cli@latest");
|
|
10629
10700
|
process.exit(1);
|
|
10701
|
+
return;
|
|
10630
10702
|
}
|
|
10631
10703
|
log(` Latest version: ${latestVersion}`);
|
|
10632
10704
|
if (currentVersion === latestVersion) {
|
|
@@ -10638,7 +10710,7 @@ async function update() {
|
|
|
10638
10710
|
\uD83D\uDCE6 Updating to latest version...
|
|
10639
10711
|
`);
|
|
10640
10712
|
try {
|
|
10641
|
-
const { stdout } = await
|
|
10713
|
+
const { stdout } = await d.exec("npm install -g chatroom-cli@latest");
|
|
10642
10714
|
if (stdout) {
|
|
10643
10715
|
log(stdout);
|
|
10644
10716
|
}
|
|
@@ -10653,52 +10725,231 @@ async function update() {
|
|
|
10653
10725
|
Try running manually with sudo:`);
|
|
10654
10726
|
console.error(" sudo npm install -g chatroom-cli@latest");
|
|
10655
10727
|
process.exit(1);
|
|
10728
|
+
return;
|
|
10656
10729
|
}
|
|
10657
10730
|
}
|
|
10658
|
-
var execAsync
|
|
10731
|
+
var execAsync;
|
|
10659
10732
|
var init_update = __esm(() => {
|
|
10660
10733
|
init_version();
|
|
10661
10734
|
execAsync = promisify(exec);
|
|
10662
|
-
log = console.log.bind(console);
|
|
10663
10735
|
});
|
|
10664
10736
|
|
|
10665
|
-
// src/commands/
|
|
10666
|
-
var
|
|
10667
|
-
__export(
|
|
10668
|
-
|
|
10737
|
+
// src/commands/init/index.ts
|
|
10738
|
+
var exports_init = {};
|
|
10739
|
+
__export(exports_init, {
|
|
10740
|
+
init: () => init
|
|
10669
10741
|
});
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
const
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10742
|
+
import path from "path";
|
|
10743
|
+
async function createDefaultDeps5() {
|
|
10744
|
+
const fs = await import("fs/promises");
|
|
10745
|
+
return {
|
|
10746
|
+
fs: {
|
|
10747
|
+
access: async (p) => {
|
|
10748
|
+
await fs.access(p);
|
|
10749
|
+
},
|
|
10750
|
+
readFile: async (p, encoding) => {
|
|
10751
|
+
return fs.readFile(p, encoding);
|
|
10752
|
+
},
|
|
10753
|
+
writeFile: async (p, content, encoding) => {
|
|
10754
|
+
await fs.writeFile(p, content, encoding);
|
|
10683
10755
|
}
|
|
10684
|
-
console.error(`
|
|
10685
|
-
To use a different environment, set CHATROOM_CONVEX_URL:`);
|
|
10686
|
-
console.error(` CHATROOM_CONVEX_URL=${otherUrls[0]} chatroom register-agent ...`);
|
|
10687
|
-
console.error(`
|
|
10688
|
-
Or to authenticate for the current environment:`);
|
|
10689
10756
|
}
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10757
|
+
};
|
|
10758
|
+
}
|
|
10759
|
+
async function fileExists(fsOps, filePath) {
|
|
10760
|
+
try {
|
|
10761
|
+
await fsOps.access(filePath);
|
|
10762
|
+
return true;
|
|
10763
|
+
} catch {
|
|
10764
|
+
return false;
|
|
10696
10765
|
}
|
|
10697
|
-
|
|
10766
|
+
}
|
|
10767
|
+
function hasIntegrationSection(content) {
|
|
10768
|
+
return content.includes("<chatroom>");
|
|
10769
|
+
}
|
|
10770
|
+
function replaceIntegrationSection(content, newSection) {
|
|
10771
|
+
const start = content.indexOf("<chatroom>");
|
|
10772
|
+
const end = content.indexOf("</chatroom>");
|
|
10773
|
+
if (start === -1 || end === -1) {
|
|
10774
|
+
return content + `
|
|
10775
|
+
|
|
10776
|
+
` + newSection;
|
|
10777
|
+
}
|
|
10778
|
+
const before = content.slice(0, start).replace(/\s+$/, "");
|
|
10779
|
+
return before + `
|
|
10780
|
+
|
|
10781
|
+
` + newSection;
|
|
10782
|
+
}
|
|
10783
|
+
async function init(options = {}, deps) {
|
|
10784
|
+
const d = deps ?? await createDefaultDeps5();
|
|
10785
|
+
const targetDir = options.dir ?? process.cwd();
|
|
10786
|
+
const result = {
|
|
10787
|
+
filesModified: [],
|
|
10788
|
+
filesSkipped: [],
|
|
10789
|
+
filesCreated: []
|
|
10790
|
+
};
|
|
10791
|
+
const foundFiles = [];
|
|
10792
|
+
for (const filename of SUPPORTED_FILES) {
|
|
10793
|
+
const filePath = path.join(targetDir, filename);
|
|
10794
|
+
if (await fileExists(d.fs, filePath)) {
|
|
10795
|
+
foundFiles.push(filename);
|
|
10796
|
+
}
|
|
10797
|
+
}
|
|
10798
|
+
if (foundFiles.length === 0) {
|
|
10799
|
+
const agentsPath = path.join(targetDir, "AGENTS.md");
|
|
10800
|
+
try {
|
|
10801
|
+
await d.fs.writeFile(agentsPath, SECTION_6_TEXT, "utf-8");
|
|
10802
|
+
result.filesCreated.push("AGENTS.md");
|
|
10803
|
+
console.log("✅ Created AGENTS.md with CHATROOM INTEGRATION section");
|
|
10804
|
+
} catch (err) {
|
|
10805
|
+
console.error(`❌ Failed to create AGENTS.md: ${err}`);
|
|
10806
|
+
}
|
|
10807
|
+
} else {
|
|
10808
|
+
for (const filename of foundFiles) {
|
|
10809
|
+
const filePath = path.join(targetDir, filename);
|
|
10810
|
+
let content;
|
|
10811
|
+
try {
|
|
10812
|
+
content = await d.fs.readFile(filePath, "utf-8");
|
|
10813
|
+
} catch (err) {
|
|
10814
|
+
console.error(`❌ Failed to read ${filename}: ${err}`);
|
|
10815
|
+
continue;
|
|
10816
|
+
}
|
|
10817
|
+
if (hasIntegrationSection(content)) {
|
|
10818
|
+
try {
|
|
10819
|
+
const updatedContent = replaceIntegrationSection(content, SECTION_6_TEXT);
|
|
10820
|
+
await d.fs.writeFile(filePath, updatedContent, "utf-8");
|
|
10821
|
+
result.filesModified.push(filename);
|
|
10822
|
+
console.log(`✅ Updated CHATROOM INTEGRATION section in ${filename}`);
|
|
10823
|
+
} catch (err) {
|
|
10824
|
+
console.error(`❌ Failed to update ${filename}: ${err}`);
|
|
10825
|
+
}
|
|
10826
|
+
} else {
|
|
10827
|
+
try {
|
|
10828
|
+
const appendText = `
|
|
10829
|
+
|
|
10830
|
+
---
|
|
10831
|
+
|
|
10832
|
+
` + SECTION_6_TEXT;
|
|
10833
|
+
await d.fs.writeFile(filePath, content + appendText, "utf-8");
|
|
10834
|
+
result.filesModified.push(filename);
|
|
10835
|
+
console.log(`✅ Added CHATROOM INTEGRATION section to ${filename}`);
|
|
10836
|
+
} catch (err) {
|
|
10837
|
+
console.error(`❌ Failed to update ${filename}: ${err}`);
|
|
10838
|
+
}
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
console.log("");
|
|
10843
|
+
console.log("Summary:");
|
|
10844
|
+
if (result.filesCreated.length > 0) {
|
|
10845
|
+
console.log(` Created : ${result.filesCreated.join(", ")}`);
|
|
10846
|
+
}
|
|
10847
|
+
if (result.filesModified.length > 0) {
|
|
10848
|
+
console.log(` Updated : ${result.filesModified.join(", ")}`);
|
|
10849
|
+
}
|
|
10850
|
+
if (result.filesSkipped.length > 0) {
|
|
10851
|
+
console.log(` Skipped : ${result.filesSkipped.join(", ")} (already has integration section)`);
|
|
10852
|
+
}
|
|
10853
|
+
const totalActions = result.filesCreated.length + result.filesModified.length + result.filesSkipped.length;
|
|
10854
|
+
if (totalActions === 0) {
|
|
10855
|
+
console.log(" Nothing to do.");
|
|
10856
|
+
}
|
|
10857
|
+
return result;
|
|
10858
|
+
}
|
|
10859
|
+
var SUPPORTED_FILES, SECTION_6_TEXT = `<chatroom>
|
|
10860
|
+
## CHATROOM INTEGRATION
|
|
10861
|
+
|
|
10862
|
+
### Workflow Loop
|
|
10863
|
+
|
|
10864
|
+
\`\`\`
|
|
10865
|
+
get-next-task → do work → handoff → get-next-task → repeat
|
|
10866
|
+
\`\`\`
|
|
10867
|
+
|
|
10868
|
+
1. **Run \`get-next-task\`** immediately via \`bash\` — block until task arrives
|
|
10869
|
+
2. **Process** the task
|
|
10870
|
+
3. **Run \`handoff\`** to signal completion
|
|
10871
|
+
4. **Run \`get-next-task\`** again to wait for next task
|
|
10872
|
+
|
|
10873
|
+
### Reliability
|
|
10874
|
+
|
|
10875
|
+
Always add these as the **last two todo items** for every task:
|
|
10876
|
+
|
|
10877
|
+
1. **Handoff item** — contains the full handoff command (see Command Reference)
|
|
10878
|
+
2. **Get-next-task item** — contains the full get-next-task command (see Command Reference)
|
|
10879
|
+
|
|
10880
|
+
This ensures you never forget to pass control back — just copy the command from the todo item and run it via \`bash\`.
|
|
10881
|
+
|
|
10882
|
+
### Command Reference
|
|
10883
|
+
|
|
10884
|
+
| Action | Command |
|
|
10885
|
+
|--------|---------|
|
|
10886
|
+
| Fetch next task | \`CHATROOM_CONVEX_URL=<endpoint> chatroom get-next-task --chatroom-id=<id> --role=<role>\` |
|
|
10887
|
+
| Pass control | \`CHATROOM_CONVEX_URL=<endpoint> chatroom handoff --chatroom-id=<id> --role=<role> --next-role=<role>\` |
|
|
10888
|
+
| Refresh system prompt (after compaction) | \`CHATROOM_CONVEX_URL=<endpoint> chatroom get-system-prompt --chatroom-id=<id> --role=<role>\` |
|
|
10889
|
+
|
|
10890
|
+
### Context Recovery
|
|
10891
|
+
|
|
10892
|
+
If you suspect compaction (context starts with "Summary of:", or you're unsure of your role):
|
|
10893
|
+
|
|
10894
|
+
1. Run \`get-system-prompt\` to reload full instructions
|
|
10895
|
+
2. Check todo list for last known step
|
|
10896
|
+
3. Resume with \`get-next-task\` or \`handoff\`
|
|
10897
|
+
</chatroom>`;
|
|
10898
|
+
var init_init = __esm(() => {
|
|
10899
|
+
SUPPORTED_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
10900
|
+
});
|
|
10901
|
+
|
|
10902
|
+
// src/commands/register-agent/index.ts
|
|
10903
|
+
var exports_register_agent = {};
|
|
10904
|
+
__export(exports_register_agent, {
|
|
10905
|
+
registerAgent: () => registerAgent
|
|
10906
|
+
});
|
|
10907
|
+
async function createDefaultDeps6() {
|
|
10908
|
+
const client2 = await getConvexClient();
|
|
10909
|
+
return {
|
|
10910
|
+
backend: {
|
|
10911
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
10912
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
10913
|
+
},
|
|
10914
|
+
session: {
|
|
10915
|
+
getSessionId,
|
|
10916
|
+
getConvexUrl,
|
|
10917
|
+
getOtherSessionUrls
|
|
10918
|
+
}
|
|
10919
|
+
};
|
|
10920
|
+
}
|
|
10921
|
+
async function registerAgent(chatroomId, options, deps) {
|
|
10922
|
+
const d = deps ?? await createDefaultDeps6();
|
|
10923
|
+
const { role, type } = options;
|
|
10924
|
+
const sessionId = d.session.getSessionId();
|
|
10925
|
+
if (!sessionId) {
|
|
10926
|
+
const otherUrls = d.session.getOtherSessionUrls();
|
|
10927
|
+
const currentUrl = d.session.getConvexUrl();
|
|
10928
|
+
console.error(`❌ Not authenticated for: ${currentUrl}`);
|
|
10929
|
+
if (otherUrls.length > 0) {
|
|
10930
|
+
console.error(`
|
|
10931
|
+
\uD83D\uDCA1 You have sessions for other environments:`);
|
|
10932
|
+
for (const url of otherUrls) {
|
|
10933
|
+
console.error(` • ${url}`);
|
|
10934
|
+
}
|
|
10935
|
+
console.error(`
|
|
10936
|
+
To use a different environment, set CHATROOM_CONVEX_URL:`);
|
|
10937
|
+
console.error(` CHATROOM_CONVEX_URL=${otherUrls[0]} chatroom register-agent ...`);
|
|
10938
|
+
console.error(`
|
|
10939
|
+
Or to authenticate for the current environment:`);
|
|
10940
|
+
}
|
|
10941
|
+
console.error(` chatroom auth login`);
|
|
10942
|
+
process.exit(1);
|
|
10943
|
+
}
|
|
10944
|
+
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
10945
|
+
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
10946
|
+
process.exit(1);
|
|
10947
|
+
}
|
|
10948
|
+
if (!/^[a-zA-Z0-9_]+$/.test(chatroomId)) {
|
|
10698
10949
|
console.error(`❌ Invalid chatroom ID format: ID must contain only alphanumeric characters and underscores`);
|
|
10699
10950
|
process.exit(1);
|
|
10700
10951
|
}
|
|
10701
|
-
const chatroom = await
|
|
10952
|
+
const chatroom = await d.backend.query(api.chatrooms.get, {
|
|
10702
10953
|
sessionId,
|
|
10703
10954
|
chatroomId
|
|
10704
10955
|
});
|
|
@@ -10711,15 +10962,10 @@ async function registerAgent(chatroomId, options) {
|
|
|
10711
10962
|
const machineInfo = ensureMachineRegistered();
|
|
10712
10963
|
let availableModels = [];
|
|
10713
10964
|
try {
|
|
10714
|
-
const
|
|
10715
|
-
|
|
10716
|
-
if (driver.capabilities.dynamicModelDiscovery) {
|
|
10717
|
-
const models = await driver.listModels();
|
|
10718
|
-
availableModels = availableModels.concat(models);
|
|
10719
|
-
}
|
|
10720
|
-
}
|
|
10965
|
+
const agentService = new OpenCodeAgentService;
|
|
10966
|
+
availableModels = await agentService.listModels();
|
|
10721
10967
|
} catch {}
|
|
10722
|
-
await
|
|
10968
|
+
await d.backend.mutation(api.machines.register, {
|
|
10723
10969
|
sessionId,
|
|
10724
10970
|
machineId: machineInfo.machineId,
|
|
10725
10971
|
hostname: machineInfo.hostname,
|
|
@@ -10729,7 +10975,7 @@ async function registerAgent(chatroomId, options) {
|
|
|
10729
10975
|
availableModels
|
|
10730
10976
|
});
|
|
10731
10977
|
const agentHarness = machineInfo.availableHarnesses.length > 0 ? machineInfo.availableHarnesses[0] : undefined;
|
|
10732
|
-
await
|
|
10978
|
+
await d.backend.mutation(api.machines.saveTeamAgentConfig, {
|
|
10733
10979
|
sessionId,
|
|
10734
10980
|
chatroomId,
|
|
10735
10981
|
role,
|
|
@@ -10750,7 +10996,7 @@ async function registerAgent(chatroomId, options) {
|
|
|
10750
10996
|
}
|
|
10751
10997
|
} else {
|
|
10752
10998
|
try {
|
|
10753
|
-
await
|
|
10999
|
+
await d.backend.mutation(api.machines.saveTeamAgentConfig, {
|
|
10754
11000
|
sessionId,
|
|
10755
11001
|
chatroomId,
|
|
10756
11002
|
role,
|
|
@@ -10765,14 +11011,11 @@ async function registerAgent(chatroomId, options) {
|
|
|
10765
11011
|
}
|
|
10766
11012
|
var init_register_agent = __esm(() => {
|
|
10767
11013
|
init_api3();
|
|
10768
|
-
init_agent_drivers();
|
|
10769
11014
|
init_storage();
|
|
10770
11015
|
init_client2();
|
|
10771
11016
|
init_machine();
|
|
11017
|
+
init_opencode();
|
|
10772
11018
|
});
|
|
10773
|
-
|
|
10774
|
-
// ../../services/backend/config/reliability.ts
|
|
10775
|
-
var HEARTBEAT_INTERVAL_MS = 30000, HEARTBEAT_TTL_MS = 90000, DAEMON_HEARTBEAT_INTERVAL_MS = 30000;
|
|
10776
11019
|
// ../../services/backend/prompts/base/cli/task-started/command.ts
|
|
10777
11020
|
function taskStartedCommand(params) {
|
|
10778
11021
|
const prefix = params.cliEnvPrefix || "";
|
|
@@ -10780,7 +11023,7 @@ function taskStartedCommand(params) {
|
|
|
10780
11023
|
const role = params.role || "<role>";
|
|
10781
11024
|
const taskId = params.taskId || "<task-id>";
|
|
10782
11025
|
const classification = params.classification || "<question|new_feature|follow_up>";
|
|
10783
|
-
const baseCmd = `${prefix}chatroom task-started --chatroom-id
|
|
11026
|
+
const baseCmd = `${prefix}chatroom task-started --chatroom-id="${chatroomId}" --role="${role}" --task-id="${taskId}" --origin-message-classification=${classification}`;
|
|
10784
11027
|
if (params.classification === "new_feature" || classification === "new_feature") {
|
|
10785
11028
|
const title = params.title || "[Feature title]";
|
|
10786
11029
|
const description = params.description || "[Feature description]";
|
|
@@ -10812,13 +11055,13 @@ var init_task_started = __esm(() => {
|
|
|
10812
11055
|
init_main_prompt();
|
|
10813
11056
|
});
|
|
10814
11057
|
|
|
10815
|
-
// ../../services/backend/prompts/base/cli/
|
|
10816
|
-
function
|
|
11058
|
+
// ../../services/backend/prompts/base/cli/get-next-task/reminder.ts
|
|
11059
|
+
function getNextTaskGuidance() {
|
|
10817
11060
|
return `\uD83D\uDD17 STAYING CONNECTED TO YOUR TEAM
|
|
10818
11061
|
|
|
10819
11062
|
Your primary directive: Stay available to respond to user and team requests.
|
|
10820
11063
|
|
|
10821
|
-
⚠️ CRITICAL: Run
|
|
11064
|
+
⚠️ CRITICAL: Run get-next-task in the FOREGROUND
|
|
10822
11065
|
|
|
10823
11066
|
Two requirements:
|
|
10824
11067
|
|
|
@@ -10831,16 +11074,27 @@ Two requirements:
|
|
|
10831
11074
|
• Only proceed after the command exits (signal or task received)
|
|
10832
11075
|
|
|
10833
11076
|
⚠️ WHEN THE PROCESS IS TERMINATED OR TIMED OUT
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
11077
|
+
|
|
11078
|
+
\`\`\`
|
|
11079
|
+
@startuml
|
|
11080
|
+
start
|
|
11081
|
+
:Command terminated unexpectedly;
|
|
11082
|
+
if (Urgent pending work?) then (yes)
|
|
11083
|
+
:Finish urgent work;
|
|
11084
|
+
:Reconnect with get-next-task;
|
|
11085
|
+
else (no)
|
|
11086
|
+
:Reconnect immediately;
|
|
11087
|
+
note right: Team cannot reach you without it
|
|
11088
|
+
endif
|
|
11089
|
+
stop
|
|
11090
|
+
@enduml
|
|
11091
|
+
\`\`\`
|
|
10839
11092
|
|
|
10840
11093
|
\uD83D\uDCCB BACKLOG TASKS
|
|
10841
11094
|
chatroom backlog list --chatroom-id=<chatroomId> --role=<role> --status=backlog
|
|
10842
11095
|
chatroom backlog --help`;
|
|
10843
11096
|
}
|
|
11097
|
+
var init_reminder = () => {};
|
|
10844
11098
|
// ../../services/backend/prompts/config/index.ts
|
|
10845
11099
|
class PromptConfigImpl {
|
|
10846
11100
|
getConvexURL() {
|
|
@@ -10893,30 +11147,262 @@ var init_getting_started_content = __esm(() => {
|
|
|
10893
11147
|
// ../../services/backend/prompts/base/cli/index.ts
|
|
10894
11148
|
var init_cli = __esm(() => {
|
|
10895
11149
|
init_task_started();
|
|
11150
|
+
init_reminder();
|
|
10896
11151
|
init_getting_started_content();
|
|
10897
11152
|
});
|
|
10898
11153
|
|
|
10899
|
-
// ../../services/backend/
|
|
10900
|
-
|
|
11154
|
+
// ../../services/backend/config/errorCodes.ts
|
|
11155
|
+
var BACKEND_ERROR_CODES, FATAL_ERROR_CODES;
|
|
11156
|
+
var init_errorCodes = __esm(() => {
|
|
11157
|
+
BACKEND_ERROR_CODES = {
|
|
11158
|
+
PARTICIPANT_NOT_FOUND: "PARTICIPANT_NOT_FOUND",
|
|
11159
|
+
CHATROOM_NOT_FOUND: "CHATROOM_NOT_FOUND",
|
|
11160
|
+
SESSION_INVALID: "SESSION_INVALID"
|
|
11161
|
+
};
|
|
11162
|
+
FATAL_ERROR_CODES = [
|
|
11163
|
+
BACKEND_ERROR_CODES.PARTICIPANT_NOT_FOUND,
|
|
11164
|
+
BACKEND_ERROR_CODES.CHATROOM_NOT_FOUND,
|
|
11165
|
+
BACKEND_ERROR_CODES.SESSION_INVALID
|
|
11166
|
+
];
|
|
11167
|
+
});
|
|
11168
|
+
|
|
11169
|
+
// ../../services/backend/prompts/base/cli/get-next-task/command.ts
|
|
11170
|
+
function getNextTaskCommand(params) {
|
|
10901
11171
|
const prefix = params.cliEnvPrefix || "";
|
|
10902
11172
|
const chatroomId = params.chatroomId || "<chatroom-id>";
|
|
10903
11173
|
const role = params.role || "<role>";
|
|
10904
|
-
return `${prefix}chatroom
|
|
10905
|
-
}
|
|
11174
|
+
return `${prefix}chatroom get-next-task --chatroom-id="${chatroomId}" --role="${role}"`;
|
|
11175
|
+
}
|
|
11176
|
+
var init_command = () => {};
|
|
11177
|
+
|
|
11178
|
+
// src/commands/get-next-task/session.ts
|
|
11179
|
+
class GetNextTaskSession {
|
|
11180
|
+
unsubscribe = null;
|
|
11181
|
+
cleanedUp = false;
|
|
11182
|
+
taskProcessed = false;
|
|
11183
|
+
fatalExitTriggered = false;
|
|
11184
|
+
chatroomId;
|
|
11185
|
+
role;
|
|
11186
|
+
silent;
|
|
11187
|
+
sessionId;
|
|
11188
|
+
connectionId;
|
|
11189
|
+
cliEnvPrefix;
|
|
11190
|
+
client;
|
|
11191
|
+
constructor(params) {
|
|
11192
|
+
this.chatroomId = params.chatroomId;
|
|
11193
|
+
this.role = params.role;
|
|
11194
|
+
this.silent = params.silent;
|
|
11195
|
+
this.sessionId = params.sessionId;
|
|
11196
|
+
this.connectionId = params.connectionId;
|
|
11197
|
+
this.cliEnvPrefix = params.cliEnvPrefix;
|
|
11198
|
+
this.client = params.client;
|
|
11199
|
+
}
|
|
11200
|
+
async start() {
|
|
11201
|
+
this.registerSignalHandlers();
|
|
11202
|
+
await this.subscribe();
|
|
11203
|
+
}
|
|
11204
|
+
logAndExit(exitCode, event, message, guidance) {
|
|
11205
|
+
const timestamp = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11206
|
+
const safeEvent = sanitizeForTerminal(event);
|
|
11207
|
+
const safeMessage = sanitizeForTerminal(message);
|
|
11208
|
+
const safeGuidance = sanitizeForTerminal(guidance);
|
|
11209
|
+
console.log(`
|
|
11210
|
+
${"─".repeat(50)}`);
|
|
11211
|
+
console.log(`[EVENT: ${safeEvent}]
|
|
11212
|
+
`);
|
|
11213
|
+
console.log(`[${timestamp}] ${safeMessage}
|
|
11214
|
+
`);
|
|
11215
|
+
if (safeGuidance) {
|
|
11216
|
+
console.log(safeGuidance);
|
|
11217
|
+
}
|
|
11218
|
+
console.log(`
|
|
11219
|
+
To reconnect, run:`);
|
|
11220
|
+
console.log(getNextTaskCommand({
|
|
11221
|
+
chatroomId: this.chatroomId,
|
|
11222
|
+
role: this.role,
|
|
11223
|
+
cliEnvPrefix: this.cliEnvPrefix
|
|
11224
|
+
}));
|
|
11225
|
+
console.log(`${"─".repeat(50)}`);
|
|
11226
|
+
this.cleanup();
|
|
11227
|
+
process.exit(exitCode);
|
|
11228
|
+
}
|
|
11229
|
+
async cleanup() {
|
|
11230
|
+
if (this.cleanedUp)
|
|
11231
|
+
return;
|
|
11232
|
+
this.cleanedUp = true;
|
|
11233
|
+
if (this.unsubscribe) {
|
|
11234
|
+
this.unsubscribe();
|
|
11235
|
+
}
|
|
11236
|
+
try {
|
|
11237
|
+
await this.client.mutation(api.participants.join, {
|
|
11238
|
+
sessionId: this.sessionId,
|
|
11239
|
+
chatroomId: this.chatroomId,
|
|
11240
|
+
role: this.role,
|
|
11241
|
+
action: "get-next-task:stopped"
|
|
11242
|
+
});
|
|
11243
|
+
} catch {}
|
|
11244
|
+
try {
|
|
11245
|
+
await this.client.mutation(api.participants.leave, {
|
|
11246
|
+
sessionId: this.sessionId,
|
|
11247
|
+
chatroomId: this.chatroomId,
|
|
11248
|
+
role: this.role
|
|
11249
|
+
});
|
|
11250
|
+
} catch {}
|
|
11251
|
+
}
|
|
11252
|
+
async subscribe() {
|
|
11253
|
+
const wsClient2 = await getConvexWsClient();
|
|
11254
|
+
this.unsubscribe = wsClient2.onUpdate(api.tasks.getPendingTasksForRole, {
|
|
11255
|
+
sessionId: this.sessionId,
|
|
11256
|
+
chatroomId: this.chatroomId,
|
|
11257
|
+
role: this.role,
|
|
11258
|
+
connectionId: this.connectionId
|
|
11259
|
+
}, (response) => {
|
|
11260
|
+
this.handleSubscriptionResponse(response).catch((error) => {
|
|
11261
|
+
console.error(`❌ Error processing task: ${error.message}`);
|
|
11262
|
+
});
|
|
11263
|
+
}, (error) => {
|
|
11264
|
+
this.handleSubscriptionError(error, "task subscription");
|
|
11265
|
+
});
|
|
11266
|
+
}
|
|
11267
|
+
async handleSubscriptionResponse(response) {
|
|
11268
|
+
if (this.taskProcessed)
|
|
11269
|
+
return;
|
|
11270
|
+
switch (response.type) {
|
|
11271
|
+
case "no_tasks":
|
|
11272
|
+
return;
|
|
11273
|
+
case "superseded":
|
|
11274
|
+
this.handleSuperseded();
|
|
11275
|
+
return;
|
|
11276
|
+
case "grace_period":
|
|
11277
|
+
this.handleGracePeriod(response);
|
|
11278
|
+
return;
|
|
11279
|
+
case "error":
|
|
11280
|
+
this.handleError(response);
|
|
11281
|
+
return;
|
|
11282
|
+
case "reconnect":
|
|
11283
|
+
this.handleReconnect(response);
|
|
11284
|
+
return;
|
|
11285
|
+
case "tasks":
|
|
11286
|
+
await this.handleTasks(response);
|
|
11287
|
+
return;
|
|
11288
|
+
}
|
|
11289
|
+
}
|
|
11290
|
+
handleSuperseded() {
|
|
11291
|
+
this.logAndExit(0, "superseded", "Another get-next-task process started for this role.", `Impact: This process is being replaced by the newer connection.
|
|
11292
|
+
` + "Action: This is expected if you started a new get-next-task session.");
|
|
11293
|
+
}
|
|
11294
|
+
handleGracePeriod(response) {
|
|
11295
|
+
const remainingSec = Math.ceil(response.remainingMs / 1000);
|
|
11296
|
+
this.logAndExit(0, "grace_period", `Task ${response.taskId} was recently acknowledged (${remainingSec}s grace remaining).`, `Impact: Another agent may still be processing this task.
|
|
11297
|
+
` + "Action: Wait for the grace period to expire, then reconnect.");
|
|
11298
|
+
}
|
|
11299
|
+
handleReconnect(response) {
|
|
11300
|
+
this.logAndExit(0, "reconnect", `Backend requested reconnect: ${response.reason}`, "Action: Reconnect to resume listening for tasks.");
|
|
11301
|
+
}
|
|
11302
|
+
handleError(response) {
|
|
11303
|
+
if (response.fatal) {
|
|
11304
|
+
if (this.fatalExitTriggered)
|
|
11305
|
+
return;
|
|
11306
|
+
this.fatalExitTriggered = true;
|
|
11307
|
+
this.logAndExit(1, "error (fatal)", `❌ FATAL ERROR — [${response.code}] ${response.message}`, "This is an unrecoverable error. The process will now exit.");
|
|
11308
|
+
}
|
|
11309
|
+
this.logAndExit(0, "error (non-fatal)", `⚠️ Non-fatal error: [${response.code}] ${response.message}`, "Action: Reconnect to resume listening for tasks.");
|
|
11310
|
+
}
|
|
11311
|
+
handleSubscriptionError(error, source) {
|
|
11312
|
+
if (error instanceof ConvexError) {
|
|
11313
|
+
const data = error.data;
|
|
11314
|
+
if (data?.code && FATAL_ERROR_CODES.includes(data.code)) {
|
|
11315
|
+
if (this.fatalExitTriggered)
|
|
11316
|
+
return;
|
|
11317
|
+
this.fatalExitTriggered = true;
|
|
11318
|
+
this.logAndExit(1, "subscription_error (fatal)", `❌ FATAL ERROR in ${source} — [${data.code}] ${data.message}`, "This is an unrecoverable error. The process will now exit.");
|
|
11319
|
+
return;
|
|
11320
|
+
}
|
|
11321
|
+
this.logAndExit(0, "subscription_error (non-fatal)", `⚠️ Non-fatal error in ${source}: [${data?.code}] ${data?.message ?? error.message}`, "Action: Reconnect to resume listening for tasks.");
|
|
11322
|
+
return;
|
|
11323
|
+
}
|
|
11324
|
+
this.logAndExit(0, "subscription_error (transient)", `⚠️ Transient error in ${source}: ${error.message}`, "Action: Reconnect to resume listening for tasks.");
|
|
11325
|
+
}
|
|
11326
|
+
async handleTasks(response) {
|
|
11327
|
+
const pendingTasks = response.tasks;
|
|
11328
|
+
const taskWithMessage = pendingTasks.length > 0 ? pendingTasks[0] : null;
|
|
11329
|
+
if (!taskWithMessage) {
|
|
11330
|
+
return;
|
|
11331
|
+
}
|
|
11332
|
+
const { task, message } = taskWithMessage;
|
|
11333
|
+
if (task.status === "pending") {
|
|
11334
|
+
try {
|
|
11335
|
+
await this.client.mutation(api.tasks.claimTask, {
|
|
11336
|
+
sessionId: this.sessionId,
|
|
11337
|
+
chatroomId: this.chatroomId,
|
|
11338
|
+
role: this.role,
|
|
11339
|
+
taskId: task._id
|
|
11340
|
+
});
|
|
11341
|
+
} catch (_claimError) {
|
|
11342
|
+
console.log(`\uD83D\uDD04 Task already claimed by another agent, continuing to wait...`);
|
|
11343
|
+
return;
|
|
11344
|
+
}
|
|
11345
|
+
}
|
|
11346
|
+
this.taskProcessed = true;
|
|
11347
|
+
if (this.unsubscribe)
|
|
11348
|
+
this.unsubscribe();
|
|
11349
|
+
this.cleanedUp = true;
|
|
11350
|
+
try {
|
|
11351
|
+
if (message) {
|
|
11352
|
+
await this.client.mutation(api.messages.claimMessage, {
|
|
11353
|
+
sessionId: this.sessionId,
|
|
11354
|
+
messageId: message._id,
|
|
11355
|
+
role: this.role
|
|
11356
|
+
});
|
|
11357
|
+
}
|
|
11358
|
+
const taskDeliveryPrompt = await this.client.query(api.messages.getTaskDeliveryPrompt, {
|
|
11359
|
+
sessionId: this.sessionId,
|
|
11360
|
+
chatroomId: this.chatroomId,
|
|
11361
|
+
role: this.role,
|
|
11362
|
+
taskId: task._id,
|
|
11363
|
+
messageId: message?._id,
|
|
11364
|
+
convexUrl: getConvexUrl()
|
|
11365
|
+
});
|
|
11366
|
+
const taskReceivedTime = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11367
|
+
console.log(`
|
|
11368
|
+
[${taskReceivedTime}] \uD83D\uDCE8 Task received!
|
|
11369
|
+
`);
|
|
11370
|
+
console.log(sanitizeForTerminal(taskDeliveryPrompt.fullCliOutput));
|
|
11371
|
+
process.exit(0);
|
|
11372
|
+
} catch (deliveryError) {
|
|
11373
|
+
this.logAndExit(1, "task_delivery_failed", `⚠️ TASK CLAIMED BUT DELIVERY FAILED — ${sanitizeUnknownForTerminal(deliveryError.message)}`, `Task ID: ${task._id}
|
|
11374
|
+
` + `The task has been claimed for your role.
|
|
11375
|
+
` + `Use context read to see your current task:
|
|
10906
11376
|
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
11377
|
+
` + ` ${this.cliEnvPrefix} chatroom context read --chatroom-id=${this.chatroomId} --role=${this.role}`);
|
|
11378
|
+
}
|
|
11379
|
+
}
|
|
11380
|
+
registerSignalHandlers() {
|
|
11381
|
+
const handleSignal = (_signal) => {
|
|
11382
|
+
this.logAndExit(0, `signal (${_signal})`, `Process interrupted (${_signal}).`, `Impact: You are no longer listening for tasks.
|
|
11383
|
+
` + "Action: Run the reconnect command immediately to resume availability.");
|
|
11384
|
+
};
|
|
11385
|
+
process.on("SIGINT", () => handleSignal("SIGINT"));
|
|
11386
|
+
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
11387
|
+
process.on("SIGHUP", () => handleSignal("SIGHUP"));
|
|
11388
|
+
}
|
|
11389
|
+
}
|
|
11390
|
+
var init_session = __esm(() => {
|
|
11391
|
+
init_errorCodes();
|
|
11392
|
+
init_command();
|
|
11393
|
+
init_values();
|
|
11394
|
+
init_api3();
|
|
11395
|
+
init_client2();
|
|
10912
11396
|
});
|
|
10913
11397
|
|
|
10914
|
-
// src/commands/
|
|
10915
|
-
var
|
|
10916
|
-
__export(
|
|
10917
|
-
|
|
11398
|
+
// src/commands/get-next-task/index.ts
|
|
11399
|
+
var exports_get_next_task = {};
|
|
11400
|
+
__export(exports_get_next_task, {
|
|
11401
|
+
getNextTask: () => getNextTask,
|
|
11402
|
+
WaitForTaskSession: () => GetNextTaskSession,
|
|
11403
|
+
GetNextTaskSession: () => GetNextTaskSession
|
|
10918
11404
|
});
|
|
10919
|
-
async function
|
|
11405
|
+
async function getNextTask(chatroomId, options) {
|
|
10920
11406
|
const client2 = await getConvexClient();
|
|
10921
11407
|
const { role, silent } = options;
|
|
10922
11408
|
const convexUrl = getConvexUrl();
|
|
@@ -10933,7 +11419,7 @@ async function waitForTask(chatroomId, options) {
|
|
|
10933
11419
|
}
|
|
10934
11420
|
console.error(`
|
|
10935
11421
|
To use a different environment, set CHATROOM_CONVEX_URL:`);
|
|
10936
|
-
console.error(` CHATROOM_CONVEX_URL=${otherUrls[0]} chatroom
|
|
11422
|
+
console.error(` CHATROOM_CONVEX_URL=${otherUrls[0]} chatroom get-next-task ...`);
|
|
10937
11423
|
console.error(`
|
|
10938
11424
|
Or to authenticate for the current environment:`);
|
|
10939
11425
|
}
|
|
@@ -10969,13 +11455,8 @@ async function waitForTask(chatroomId, options) {
|
|
|
10969
11455
|
const machineInfo = ensureMachineRegistered();
|
|
10970
11456
|
let availableModels = [];
|
|
10971
11457
|
try {
|
|
10972
|
-
const
|
|
10973
|
-
|
|
10974
|
-
if (driver.capabilities.dynamicModelDiscovery) {
|
|
10975
|
-
const models = await driver.listModels();
|
|
10976
|
-
availableModels = availableModels.concat(models);
|
|
10977
|
-
}
|
|
10978
|
-
}
|
|
11458
|
+
const agentService = new OpenCodeAgentService;
|
|
11459
|
+
availableModels = await agentService.listModels();
|
|
10979
11460
|
} catch {}
|
|
10980
11461
|
await client2.mutation(api.machines.register, {
|
|
10981
11462
|
sessionId,
|
|
@@ -10989,7 +11470,6 @@ async function waitForTask(chatroomId, options) {
|
|
|
10989
11470
|
const agentType = options.agentType ?? (machineInfo.availableHarnesses.length > 0 ? machineInfo.availableHarnesses[0] : undefined);
|
|
10990
11471
|
if (agentType) {
|
|
10991
11472
|
const workingDir = process.cwd();
|
|
10992
|
-
updateAgentContext(chatroomId, role, agentType, workingDir);
|
|
10993
11473
|
await client2.mutation(api.machines.updateAgentConfig, {
|
|
10994
11474
|
sessionId,
|
|
10995
11475
|
machineId: machineInfo.machineId,
|
|
@@ -11001,16 +11481,26 @@ async function waitForTask(chatroomId, options) {
|
|
|
11001
11481
|
}
|
|
11002
11482
|
} catch (machineError) {
|
|
11003
11483
|
if (!silent) {
|
|
11004
|
-
console.warn(`⚠️ Machine registration failed: ${machineError.message}`);
|
|
11484
|
+
console.warn(`⚠️ Machine registration failed: ${sanitizeUnknownForTerminal(machineError.message)}`);
|
|
11005
11485
|
}
|
|
11006
11486
|
}
|
|
11007
11487
|
const connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
11488
|
+
let participantAgentType;
|
|
11489
|
+
try {
|
|
11490
|
+
const teamConfigs = await client2.query(api.machines.getTeamAgentConfigs, {
|
|
11491
|
+
sessionId,
|
|
11492
|
+
chatroomId
|
|
11493
|
+
});
|
|
11494
|
+
const roleConfig = teamConfigs?.find((c) => c.role.toLowerCase() === role.toLowerCase());
|
|
11495
|
+
participantAgentType = roleConfig?.type;
|
|
11496
|
+
} catch {}
|
|
11008
11497
|
await client2.mutation(api.participants.join, {
|
|
11009
11498
|
sessionId,
|
|
11010
11499
|
chatroomId,
|
|
11011
11500
|
role,
|
|
11012
|
-
|
|
11013
|
-
connectionId
|
|
11501
|
+
action: "get-next-task:started",
|
|
11502
|
+
connectionId,
|
|
11503
|
+
agentType: participantAgentType
|
|
11014
11504
|
});
|
|
11015
11505
|
const connectionTime = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11016
11506
|
if (!silent) {
|
|
@@ -11034,7 +11524,7 @@ async function waitForTask(chatroomId, options) {
|
|
|
11034
11524
|
console.log("\uD83D\uDCCB AGENT INITIALIZATION PROMPT");
|
|
11035
11525
|
console.log("═".repeat(50));
|
|
11036
11526
|
console.log("");
|
|
11037
|
-
console.log(
|
|
11527
|
+
console.log(getNextTaskGuidance());
|
|
11038
11528
|
console.log("");
|
|
11039
11529
|
console.log("═".repeat(50));
|
|
11040
11530
|
console.log("");
|
|
@@ -11046,192 +11536,58 @@ async function waitForTask(chatroomId, options) {
|
|
|
11046
11536
|
}
|
|
11047
11537
|
}
|
|
11048
11538
|
} catch {}
|
|
11049
|
-
const
|
|
11050
|
-
client2.mutation(api.participants.heartbeat, {
|
|
11051
|
-
sessionId,
|
|
11052
|
-
chatroomId,
|
|
11053
|
-
role,
|
|
11054
|
-
connectionId
|
|
11055
|
-
}).then((result) => {
|
|
11056
|
-
if (result?.status === "rejoin_required") {
|
|
11057
|
-
if (!silent) {
|
|
11058
|
-
console.warn(`⚠️ Participant record missing — re-joining chatroom`);
|
|
11059
|
-
}
|
|
11060
|
-
return client2.mutation(api.participants.join, {
|
|
11061
|
-
sessionId,
|
|
11062
|
-
chatroomId,
|
|
11063
|
-
role,
|
|
11064
|
-
readyUntil: Date.now() + HEARTBEAT_TTL_MS,
|
|
11065
|
-
connectionId
|
|
11066
|
-
});
|
|
11067
|
-
}
|
|
11068
|
-
}).catch((err) => {
|
|
11069
|
-
if (!silent) {
|
|
11070
|
-
console.warn(`⚠️ Heartbeat failed: ${err.message}`);
|
|
11071
|
-
}
|
|
11072
|
-
});
|
|
11073
|
-
}, HEARTBEAT_INTERVAL_MS);
|
|
11074
|
-
heartbeatTimer.unref();
|
|
11075
|
-
let cleanedUp = false;
|
|
11076
|
-
const cleanup = async () => {
|
|
11077
|
-
if (cleanedUp)
|
|
11078
|
-
return;
|
|
11079
|
-
cleanedUp = true;
|
|
11080
|
-
clearInterval(heartbeatTimer);
|
|
11081
|
-
try {
|
|
11082
|
-
await client2.mutation(api.participants.leave, {
|
|
11083
|
-
sessionId,
|
|
11084
|
-
chatroomId,
|
|
11085
|
-
role
|
|
11086
|
-
});
|
|
11087
|
-
} catch {}
|
|
11088
|
-
};
|
|
11089
|
-
let taskProcessed = false;
|
|
11090
|
-
let unsubscribe = null;
|
|
11091
|
-
const handlePendingTasks = async (pendingTasks) => {
|
|
11092
|
-
if (taskProcessed)
|
|
11093
|
-
return;
|
|
11094
|
-
const currentConnectionId = await client2.query(api.participants.getConnectionId, {
|
|
11095
|
-
sessionId,
|
|
11096
|
-
chatroomId,
|
|
11097
|
-
role
|
|
11098
|
-
});
|
|
11099
|
-
if (currentConnectionId && currentConnectionId !== connectionId) {
|
|
11100
|
-
if (unsubscribe)
|
|
11101
|
-
unsubscribe();
|
|
11102
|
-
clearInterval(heartbeatTimer);
|
|
11103
|
-
cleanedUp = true;
|
|
11104
|
-
const takeoverTime = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11105
|
-
console.log(`
|
|
11106
|
-
${"─".repeat(50)}`);
|
|
11107
|
-
console.log(`⚠️ CONNECTION SUPERSEDED
|
|
11108
|
-
`);
|
|
11109
|
-
console.log(`[${takeoverTime}] Why: Another wait-for-task process started for this role`);
|
|
11110
|
-
console.log(`Impact: This process is being replaced by the newer connection`);
|
|
11111
|
-
console.log(`Action: This is expected if you started a new wait-for-task session
|
|
11112
|
-
`);
|
|
11113
|
-
console.log(`If you meant to use THIS terminal, run:`);
|
|
11114
|
-
console.log(waitForTaskCommand({ chatroomId, role, cliEnvPrefix }));
|
|
11115
|
-
console.log(`${"─".repeat(50)}`);
|
|
11116
|
-
process.exit(0);
|
|
11117
|
-
}
|
|
11118
|
-
const taskWithMessage = pendingTasks.length > 0 ? pendingTasks[0] : null;
|
|
11119
|
-
if (!taskWithMessage) {
|
|
11120
|
-
return;
|
|
11121
|
-
}
|
|
11122
|
-
const { task, message } = taskWithMessage;
|
|
11123
|
-
if (task.status === "acknowledged") {
|
|
11124
|
-
const acknowledgedAt = task.acknowledgedAt || task.updatedAt;
|
|
11125
|
-
const elapsedMs = Date.now() - acknowledgedAt;
|
|
11126
|
-
const RECOVERY_GRACE_PERIOD_MS = 60 * 1000;
|
|
11127
|
-
if (elapsedMs < RECOVERY_GRACE_PERIOD_MS) {
|
|
11128
|
-
const remainingSec = Math.ceil((RECOVERY_GRACE_PERIOD_MS - elapsedMs) / 1000);
|
|
11129
|
-
console.log(`\uD83D\uDD04 Task was recently acknowledged (${remainingSec}s remaining). ` + `Re-run wait-for-task in 1 minute to recover it if the other agent is unresponsive.`);
|
|
11130
|
-
return;
|
|
11131
|
-
}
|
|
11132
|
-
} else {
|
|
11133
|
-
try {
|
|
11134
|
-
await client2.mutation(api.tasks.claimTask, {
|
|
11135
|
-
sessionId,
|
|
11136
|
-
chatroomId,
|
|
11137
|
-
role
|
|
11138
|
-
});
|
|
11139
|
-
} catch (_claimError) {
|
|
11140
|
-
console.log(`\uD83D\uDD04 Task already claimed by another agent, continuing to wait...`);
|
|
11141
|
-
return;
|
|
11142
|
-
}
|
|
11143
|
-
}
|
|
11144
|
-
taskProcessed = true;
|
|
11145
|
-
if (message) {
|
|
11146
|
-
await client2.mutation(api.messages.claimMessage, {
|
|
11147
|
-
sessionId,
|
|
11148
|
-
messageId: message._id,
|
|
11149
|
-
role
|
|
11150
|
-
});
|
|
11151
|
-
}
|
|
11152
|
-
if (unsubscribe)
|
|
11153
|
-
unsubscribe();
|
|
11154
|
-
clearInterval(heartbeatTimer);
|
|
11155
|
-
cleanedUp = true;
|
|
11156
|
-
const activeUntil = Date.now() + DEFAULT_ACTIVE_TIMEOUT_MS;
|
|
11157
|
-
await client2.mutation(api.participants.updateStatus, {
|
|
11158
|
-
sessionId,
|
|
11159
|
-
chatroomId,
|
|
11160
|
-
role,
|
|
11161
|
-
status: "active",
|
|
11162
|
-
expiresAt: activeUntil
|
|
11163
|
-
});
|
|
11164
|
-
const taskDeliveryPrompt = await client2.query(api.messages.getTaskDeliveryPrompt, {
|
|
11165
|
-
sessionId,
|
|
11166
|
-
chatroomId,
|
|
11167
|
-
role,
|
|
11168
|
-
taskId: task._id,
|
|
11169
|
-
messageId: message?._id,
|
|
11170
|
-
convexUrl
|
|
11171
|
-
});
|
|
11172
|
-
const taskReceivedTime = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11173
|
-
console.log(`
|
|
11174
|
-
[${taskReceivedTime}] \uD83D\uDCE8 Task received!
|
|
11175
|
-
`);
|
|
11176
|
-
console.log(taskDeliveryPrompt.fullCliOutput);
|
|
11177
|
-
process.exit(0);
|
|
11178
|
-
};
|
|
11179
|
-
const wsClient2 = await getConvexWsClient();
|
|
11180
|
-
unsubscribe = wsClient2.onUpdate(api.tasks.getPendingTasksForRole, {
|
|
11181
|
-
sessionId,
|
|
11539
|
+
const session = new GetNextTaskSession({
|
|
11182
11540
|
chatroomId,
|
|
11183
|
-
role
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11541
|
+
role,
|
|
11542
|
+
silent: !!silent,
|
|
11543
|
+
sessionId,
|
|
11544
|
+
connectionId,
|
|
11545
|
+
cliEnvPrefix,
|
|
11546
|
+
client: client2
|
|
11188
11547
|
});
|
|
11189
|
-
|
|
11190
|
-
if (unsubscribe)
|
|
11191
|
-
unsubscribe();
|
|
11192
|
-
cleanup().finally(() => {
|
|
11193
|
-
const signalTime = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
11194
|
-
console.log(`
|
|
11195
|
-
${"─".repeat(50)}`);
|
|
11196
|
-
console.log(`⚠️ RECONNECTION REQUIRED
|
|
11197
|
-
`);
|
|
11198
|
-
console.log(`[${signalTime}] Why: Process interrupted (unexpected termination)`);
|
|
11199
|
-
console.log(`Impact: You are no longer listening for tasks`);
|
|
11200
|
-
console.log(`Action: Run this command immediately to resume availability
|
|
11201
|
-
`);
|
|
11202
|
-
console.log(waitForTaskCommand({ chatroomId, role, cliEnvPrefix }));
|
|
11203
|
-
console.log(`${"─".repeat(50)}`);
|
|
11204
|
-
process.exit(0);
|
|
11205
|
-
});
|
|
11206
|
-
};
|
|
11207
|
-
process.on("SIGINT", () => handleSignal("SIGINT"));
|
|
11208
|
-
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
11209
|
-
process.on("SIGHUP", () => handleSignal("SIGHUP"));
|
|
11548
|
+
await session.start();
|
|
11210
11549
|
}
|
|
11211
|
-
var
|
|
11550
|
+
var init_get_next_task = __esm(() => {
|
|
11212
11551
|
init_cli();
|
|
11213
11552
|
init_env();
|
|
11553
|
+
init_session();
|
|
11214
11554
|
init_api3();
|
|
11215
|
-
init_config2();
|
|
11216
|
-
init_agent_drivers();
|
|
11217
11555
|
init_storage();
|
|
11218
11556
|
init_client2();
|
|
11219
11557
|
init_machine();
|
|
11558
|
+
init_opencode();
|
|
11559
|
+
init_error_formatting();
|
|
11560
|
+
init_session();
|
|
11561
|
+
init_session();
|
|
11220
11562
|
});
|
|
11221
11563
|
|
|
11222
|
-
// src/commands/task-started.ts
|
|
11564
|
+
// src/commands/task-started/index.ts
|
|
11223
11565
|
var exports_task_started2 = {};
|
|
11224
11566
|
__export(exports_task_started2, {
|
|
11225
11567
|
taskStarted: () => taskStarted
|
|
11226
11568
|
});
|
|
11227
|
-
async function
|
|
11569
|
+
async function createDefaultDeps7() {
|
|
11228
11570
|
const client2 = await getConvexClient();
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11571
|
+
return {
|
|
11572
|
+
backend: {
|
|
11573
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
11574
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
11575
|
+
},
|
|
11576
|
+
session: {
|
|
11577
|
+
getSessionId,
|
|
11578
|
+
getConvexUrl,
|
|
11579
|
+
getOtherSessionUrls
|
|
11580
|
+
}
|
|
11581
|
+
};
|
|
11582
|
+
}
|
|
11583
|
+
async function taskStarted(chatroomId, options, deps) {
|
|
11584
|
+
const d = deps ?? await createDefaultDeps7();
|
|
11585
|
+
const { role, originMessageClassification, rawStdin, taskId, noClassify } = options;
|
|
11586
|
+
const convexUrl = d.session.getConvexUrl();
|
|
11587
|
+
const cliEnvPrefix = getCliEnvPrefix(convexUrl);
|
|
11588
|
+
const sessionId = d.session.getSessionId();
|
|
11233
11589
|
if (!sessionId) {
|
|
11234
|
-
const otherUrls = getOtherSessionUrls();
|
|
11590
|
+
const otherUrls = d.session.getOtherSessionUrls();
|
|
11235
11591
|
console.error(`❌ Not authenticated for: ${convexUrl}`);
|
|
11236
11592
|
if (otherUrls.length > 0) {
|
|
11237
11593
|
console.error(`
|
|
@@ -11306,7 +11662,7 @@ How to implement it' | ${taskStartedCommand({
|
|
|
11306
11662
|
})}`);
|
|
11307
11663
|
process.exit(1);
|
|
11308
11664
|
}
|
|
11309
|
-
targetTask = await
|
|
11665
|
+
targetTask = await d.backend.query(api.tasks.getTask, {
|
|
11310
11666
|
sessionId,
|
|
11311
11667
|
chatroomId,
|
|
11312
11668
|
taskId
|
|
@@ -11317,7 +11673,7 @@ How to implement it' | ${taskStartedCommand({
|
|
|
11317
11673
|
process.exit(1);
|
|
11318
11674
|
}
|
|
11319
11675
|
try {
|
|
11320
|
-
await
|
|
11676
|
+
await d.backend.mutation(api.tasks.startTask, {
|
|
11321
11677
|
sessionId,
|
|
11322
11678
|
chatroomId,
|
|
11323
11679
|
role,
|
|
@@ -11337,13 +11693,13 @@ How to implement it' | ${taskStartedCommand({
|
|
|
11337
11693
|
return;
|
|
11338
11694
|
}
|
|
11339
11695
|
try {
|
|
11340
|
-
const result = await
|
|
11696
|
+
const result = await d.backend.mutation(api.messages.taskStarted, {
|
|
11341
11697
|
sessionId,
|
|
11342
11698
|
chatroomId,
|
|
11343
11699
|
role,
|
|
11344
11700
|
taskId,
|
|
11345
11701
|
originMessageClassification,
|
|
11346
|
-
convexUrl: getConvexUrl(),
|
|
11702
|
+
convexUrl: d.session.getConvexUrl(),
|
|
11347
11703
|
...rawStdin && { rawStdin }
|
|
11348
11704
|
});
|
|
11349
11705
|
console.log(`✅ Task acknowledged and classified`);
|
|
@@ -11363,10 +11719,10 @@ How to implement it' | ${taskStartedCommand({
|
|
|
11363
11719
|
console.error(` Stack trace:`);
|
|
11364
11720
|
stackLines.forEach((line) => console.error(` ${line}`));
|
|
11365
11721
|
}
|
|
11366
|
-
if (typeof error === "object" && error !== null) {
|
|
11367
|
-
const
|
|
11368
|
-
if (
|
|
11369
|
-
console.error(` Server details:`, JSON.stringify(
|
|
11722
|
+
if (typeof error === "object" && error !== null && "data" in error) {
|
|
11723
|
+
const errData = error.data;
|
|
11724
|
+
if (errData) {
|
|
11725
|
+
console.error(` Server details:`, JSON.stringify(errData, null, 2));
|
|
11370
11726
|
}
|
|
11371
11727
|
}
|
|
11372
11728
|
process.exit(1);
|
|
@@ -11379,90 +11735,6 @@ var init_task_started2 = __esm(() => {
|
|
|
11379
11735
|
init_client2();
|
|
11380
11736
|
});
|
|
11381
11737
|
|
|
11382
|
-
// src/commands/task-complete.ts
|
|
11383
|
-
var exports_task_complete = {};
|
|
11384
|
-
__export(exports_task_complete, {
|
|
11385
|
-
taskComplete: () => taskComplete
|
|
11386
|
-
});
|
|
11387
|
-
async function taskComplete(chatroomId, options) {
|
|
11388
|
-
const client2 = await getConvexClient();
|
|
11389
|
-
const { role } = options;
|
|
11390
|
-
const sessionId = getSessionId();
|
|
11391
|
-
if (!sessionId) {
|
|
11392
|
-
const otherUrls = getOtherSessionUrls();
|
|
11393
|
-
const currentUrl = getConvexUrl();
|
|
11394
|
-
formatAuthError(currentUrl, otherUrls);
|
|
11395
|
-
process.exit(1);
|
|
11396
|
-
}
|
|
11397
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
11398
|
-
formatChatroomIdError(chatroomId);
|
|
11399
|
-
process.exit(1);
|
|
11400
|
-
}
|
|
11401
|
-
let result;
|
|
11402
|
-
try {
|
|
11403
|
-
result = await client2.mutation(api.tasks.completeTask, {
|
|
11404
|
-
sessionId,
|
|
11405
|
-
chatroomId,
|
|
11406
|
-
role
|
|
11407
|
-
});
|
|
11408
|
-
} catch (error) {
|
|
11409
|
-
console.error(`
|
|
11410
|
-
❌ ERROR: Task completion failed`);
|
|
11411
|
-
if (error instanceof ConvexError) {
|
|
11412
|
-
const errorData = error.data;
|
|
11413
|
-
console.error(`
|
|
11414
|
-
${errorData.message || "An unexpected error occurred"}`);
|
|
11415
|
-
if (process.env.CHATROOM_DEBUG === "true") {
|
|
11416
|
-
console.error(`
|
|
11417
|
-
\uD83D\uDD0D Debug Info:`);
|
|
11418
|
-
console.error(JSON.stringify(errorData, null, 2));
|
|
11419
|
-
}
|
|
11420
|
-
const convexUrl2 = getConvexUrl();
|
|
11421
|
-
if (errorData.code === "AUTH_FAILED") {
|
|
11422
|
-
console.error(`
|
|
11423
|
-
\uD83D\uDCA1 Try authenticating again:`);
|
|
11424
|
-
console.error(` chatroom auth ${convexUrl2}`);
|
|
11425
|
-
}
|
|
11426
|
-
} else {
|
|
11427
|
-
console.error(`
|
|
11428
|
-
${error instanceof Error ? error.message : String(error)}`);
|
|
11429
|
-
if (process.env.CHATROOM_DEBUG === "true") {
|
|
11430
|
-
console.error(`
|
|
11431
|
-
\uD83D\uDD0D Debug Info:`);
|
|
11432
|
-
console.error(error);
|
|
11433
|
-
}
|
|
11434
|
-
}
|
|
11435
|
-
console.error(`
|
|
11436
|
-
\uD83D\uDCDA Need help? Check the docs or run:`);
|
|
11437
|
-
console.error(` chatroom task-complete --help`);
|
|
11438
|
-
process.exit(1);
|
|
11439
|
-
}
|
|
11440
|
-
if (!result.completed) {
|
|
11441
|
-
formatError("No task to complete", [
|
|
11442
|
-
"Make sure you have an in_progress task before completing.",
|
|
11443
|
-
"Run `chatroom wait-for-task` to receive and start a task first."
|
|
11444
|
-
]);
|
|
11445
|
-
process.exit(1);
|
|
11446
|
-
}
|
|
11447
|
-
console.log(`✅ Task completed successfully`);
|
|
11448
|
-
console.log(` Tasks completed: ${result.completedCount}`);
|
|
11449
|
-
if (result.promoted) {
|
|
11450
|
-
console.log(` Promoted next task: ${result.promoted}`);
|
|
11451
|
-
}
|
|
11452
|
-
const convexUrl = getConvexUrl();
|
|
11453
|
-
const cliEnvPrefix = getCliEnvPrefix(convexUrl);
|
|
11454
|
-
console.log(`
|
|
11455
|
-
⏳ Now run wait-for-task to wait for your next assignment:`);
|
|
11456
|
-
console.log(` ${waitForTaskCommand({ chatroomId, role, cliEnvPrefix })}`);
|
|
11457
|
-
}
|
|
11458
|
-
var init_task_complete = __esm(() => {
|
|
11459
|
-
init_env();
|
|
11460
|
-
init_values();
|
|
11461
|
-
init_api3();
|
|
11462
|
-
init_storage();
|
|
11463
|
-
init_client2();
|
|
11464
|
-
});
|
|
11465
|
-
|
|
11466
11738
|
// src/utils/serialization/decode/index.ts
|
|
11467
11739
|
var exports_decode = {};
|
|
11468
11740
|
__export(exports_decode, {
|
|
@@ -11473,7 +11745,10 @@ __export(exports_decode, {
|
|
|
11473
11745
|
function decode(input, options = {}) {
|
|
11474
11746
|
const { singleParam, expectedParams, requiredParams } = options;
|
|
11475
11747
|
if (singleParam) {
|
|
11476
|
-
|
|
11748
|
+
const trimmed = input.trim();
|
|
11749
|
+
const singleParamDelimiter = `---${singleParam.toUpperCase()}---`;
|
|
11750
|
+
const stripped = trimmed.startsWith(singleParamDelimiter) ? trimmed.slice(singleParamDelimiter.length).trim() : trimmed;
|
|
11751
|
+
return { [singleParam]: stripped };
|
|
11477
11752
|
}
|
|
11478
11753
|
return decodeMultiParam(input, expectedParams, requiredParams);
|
|
11479
11754
|
}
|
|
@@ -11589,23 +11864,38 @@ function handoffCommand(params) {
|
|
|
11589
11864
|
const chatroomId = params.chatroomId || "<chatroom-id>";
|
|
11590
11865
|
const role = params.role || "<role>";
|
|
11591
11866
|
const nextRole = params.nextRole || "<target>";
|
|
11592
|
-
return `${prefix}chatroom handoff --chatroom-id
|
|
11867
|
+
return `${prefix}chatroom handoff --chatroom-id="${chatroomId}" --role="${role}" --next-role="${nextRole}" << 'EOF'
|
|
11868
|
+
---MESSAGE---
|
|
11593
11869
|
[Your message here]
|
|
11594
11870
|
EOF`;
|
|
11595
11871
|
}
|
|
11596
11872
|
|
|
11597
|
-
// src/commands/handoff.ts
|
|
11873
|
+
// src/commands/handoff/index.ts
|
|
11598
11874
|
var exports_handoff = {};
|
|
11599
11875
|
__export(exports_handoff, {
|
|
11600
11876
|
handoff: () => handoff
|
|
11601
11877
|
});
|
|
11602
|
-
async function
|
|
11878
|
+
async function createDefaultDeps8() {
|
|
11603
11879
|
const client2 = await getConvexClient();
|
|
11880
|
+
return {
|
|
11881
|
+
backend: {
|
|
11882
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
11883
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
11884
|
+
},
|
|
11885
|
+
session: {
|
|
11886
|
+
getSessionId,
|
|
11887
|
+
getConvexUrl,
|
|
11888
|
+
getOtherSessionUrls
|
|
11889
|
+
}
|
|
11890
|
+
};
|
|
11891
|
+
}
|
|
11892
|
+
async function handoff(chatroomId, options, deps) {
|
|
11893
|
+
const d = deps ?? await createDefaultDeps8();
|
|
11604
11894
|
const { role, message, nextRole, attachedArtifactIds = [] } = options;
|
|
11605
|
-
const sessionId = getSessionId();
|
|
11895
|
+
const sessionId = d.session.getSessionId();
|
|
11606
11896
|
if (!sessionId) {
|
|
11607
|
-
const otherUrls = getOtherSessionUrls();
|
|
11608
|
-
const currentUrl = getConvexUrl();
|
|
11897
|
+
const otherUrls = d.session.getOtherSessionUrls();
|
|
11898
|
+
const currentUrl = d.session.getConvexUrl();
|
|
11609
11899
|
formatAuthError(currentUrl, otherUrls);
|
|
11610
11900
|
process.exit(1);
|
|
11611
11901
|
}
|
|
@@ -11615,7 +11905,7 @@ async function handoff(chatroomId, options) {
|
|
|
11615
11905
|
}
|
|
11616
11906
|
if (attachedArtifactIds.length > 0) {
|
|
11617
11907
|
try {
|
|
11618
|
-
const areValid = await
|
|
11908
|
+
const areValid = await d.backend.query(api.artifacts.validateArtifactIds, {
|
|
11619
11909
|
sessionId,
|
|
11620
11910
|
artifactIds: attachedArtifactIds
|
|
11621
11911
|
});
|
|
@@ -11633,12 +11923,15 @@ async function handoff(chatroomId, options) {
|
|
|
11633
11923
|
}
|
|
11634
11924
|
let result;
|
|
11635
11925
|
try {
|
|
11636
|
-
result = await
|
|
11926
|
+
result = await d.backend.mutation(api.messages.sendHandoff, {
|
|
11637
11927
|
sessionId,
|
|
11638
11928
|
chatroomId,
|
|
11639
11929
|
senderRole: role,
|
|
11640
11930
|
content: message,
|
|
11641
|
-
targetRole: nextRole
|
|
11931
|
+
targetRole: nextRole,
|
|
11932
|
+
...attachedArtifactIds.length > 0 && {
|
|
11933
|
+
attachedArtifactIds
|
|
11934
|
+
}
|
|
11642
11935
|
});
|
|
11643
11936
|
} catch (error) {
|
|
11644
11937
|
console.error(`
|
|
@@ -11652,7 +11945,7 @@ ${errorData.message || "An unexpected error occurred"}`);
|
|
|
11652
11945
|
\uD83D\uDD0D Debug Info:`);
|
|
11653
11946
|
console.error(JSON.stringify(errorData, null, 2));
|
|
11654
11947
|
}
|
|
11655
|
-
const convexUrl2 = getConvexUrl();
|
|
11948
|
+
const convexUrl2 = d.session.getConvexUrl();
|
|
11656
11949
|
if (errorData.code === "AUTH_FAILED") {
|
|
11657
11950
|
console.error(`
|
|
11658
11951
|
\uD83D\uDCA1 Try authenticating again:`);
|
|
@@ -11674,9 +11967,10 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
11674
11967
|
\uD83D\uDCDA Need help? Check the docs or run:`);
|
|
11675
11968
|
console.error(` chatroom handoff --help`);
|
|
11676
11969
|
process.exit(1);
|
|
11970
|
+
return;
|
|
11677
11971
|
}
|
|
11678
11972
|
if (!result.success && result.error) {
|
|
11679
|
-
const convexUrl2 = getConvexUrl();
|
|
11973
|
+
const convexUrl2 = d.session.getConvexUrl();
|
|
11680
11974
|
const cliEnvPrefix2 = getCliEnvPrefix(convexUrl2);
|
|
11681
11975
|
console.error(`
|
|
11682
11976
|
❌ ERROR: ${result.error.message}`);
|
|
@@ -11702,36 +11996,47 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
11702
11996
|
console.log(` • ${id}`);
|
|
11703
11997
|
});
|
|
11704
11998
|
}
|
|
11705
|
-
|
|
11706
|
-
console.log(`
|
|
11707
|
-
\uD83C\uDF89 Workflow complete! Control returned to user.`);
|
|
11708
|
-
}
|
|
11709
|
-
const convexUrl = getConvexUrl();
|
|
11999
|
+
const convexUrl = d.session.getConvexUrl();
|
|
11710
12000
|
const cliEnvPrefix = getCliEnvPrefix(convexUrl);
|
|
11711
12001
|
console.log(`
|
|
11712
|
-
⏳
|
|
11713
|
-
console.log(` ${waitForTaskCommand({ chatroomId, role, cliEnvPrefix })}`);
|
|
12002
|
+
⏳ Next → \`${getNextTaskCommand({ chatroomId, role, cliEnvPrefix })}\``);
|
|
11714
12003
|
}
|
|
11715
12004
|
var init_handoff = __esm(() => {
|
|
12005
|
+
init_command();
|
|
11716
12006
|
init_env();
|
|
11717
12007
|
init_values();
|
|
11718
12008
|
init_api3();
|
|
11719
12009
|
init_storage();
|
|
11720
12010
|
init_client2();
|
|
12011
|
+
init_error_formatting();
|
|
11721
12012
|
});
|
|
11722
12013
|
|
|
11723
|
-
// src/commands/report-progress.ts
|
|
12014
|
+
// src/commands/report-progress/index.ts
|
|
11724
12015
|
var exports_report_progress = {};
|
|
11725
12016
|
__export(exports_report_progress, {
|
|
11726
12017
|
reportProgress: () => reportProgress
|
|
11727
12018
|
});
|
|
11728
|
-
async function
|
|
12019
|
+
async function createDefaultDeps9() {
|
|
11729
12020
|
const client2 = await getConvexClient();
|
|
12021
|
+
return {
|
|
12022
|
+
backend: {
|
|
12023
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
12024
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
12025
|
+
},
|
|
12026
|
+
session: {
|
|
12027
|
+
getSessionId,
|
|
12028
|
+
getConvexUrl,
|
|
12029
|
+
getOtherSessionUrls
|
|
12030
|
+
}
|
|
12031
|
+
};
|
|
12032
|
+
}
|
|
12033
|
+
async function reportProgress(chatroomId, options, deps) {
|
|
12034
|
+
const d = deps ?? await createDefaultDeps9();
|
|
11730
12035
|
const { role, message } = options;
|
|
11731
|
-
const sessionId = getSessionId();
|
|
12036
|
+
const sessionId = d.session.getSessionId();
|
|
11732
12037
|
if (!sessionId) {
|
|
11733
|
-
const otherUrls = getOtherSessionUrls();
|
|
11734
|
-
const currentUrl = getConvexUrl();
|
|
12038
|
+
const otherUrls = d.session.getOtherSessionUrls();
|
|
12039
|
+
const currentUrl = d.session.getConvexUrl();
|
|
11735
12040
|
formatAuthError(currentUrl, otherUrls);
|
|
11736
12041
|
process.exit(1);
|
|
11737
12042
|
}
|
|
@@ -11749,7 +12054,7 @@ async function reportProgress(chatroomId, options) {
|
|
|
11749
12054
|
process.exit(1);
|
|
11750
12055
|
}
|
|
11751
12056
|
try {
|
|
11752
|
-
const result = await
|
|
12057
|
+
const result = await d.backend.mutation(api.messages.reportProgress, {
|
|
11753
12058
|
sessionId,
|
|
11754
12059
|
chatroomId,
|
|
11755
12060
|
senderRole: role,
|
|
@@ -11771,7 +12076,7 @@ ${errorData.message || "An unexpected error occurred"}`);
|
|
|
11771
12076
|
\uD83D\uDD0D Debug Info:`);
|
|
11772
12077
|
console.error(JSON.stringify(errorData, null, 2));
|
|
11773
12078
|
}
|
|
11774
|
-
const convexUrl = getConvexUrl();
|
|
12079
|
+
const convexUrl = d.session.getConvexUrl();
|
|
11775
12080
|
if (errorData.code === "AUTH_FAILED") {
|
|
11776
12081
|
console.error(`
|
|
11777
12082
|
\uD83D\uDCA1 Try authenticating again:`);
|
|
@@ -11796,6 +12101,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
11796
12101
|
\uD83D\uDCDA Need help? Check the docs or run:`);
|
|
11797
12102
|
console.error(` chatroom report-progress --help`);
|
|
11798
12103
|
process.exit(1);
|
|
12104
|
+
return;
|
|
11799
12105
|
}
|
|
11800
12106
|
}
|
|
11801
12107
|
var init_report_progress = __esm(() => {
|
|
@@ -11803,13 +12109,13 @@ var init_report_progress = __esm(() => {
|
|
|
11803
12109
|
init_api3();
|
|
11804
12110
|
init_storage();
|
|
11805
12111
|
init_client2();
|
|
12112
|
+
init_error_formatting();
|
|
11806
12113
|
});
|
|
11807
12114
|
|
|
11808
|
-
// src/commands/backlog.ts
|
|
12115
|
+
// src/commands/backlog/index.ts
|
|
11809
12116
|
var exports_backlog = {};
|
|
11810
12117
|
__export(exports_backlog, {
|
|
11811
12118
|
scoreBacklog: () => scoreBacklog,
|
|
11812
|
-
resetBacklog: () => resetBacklog,
|
|
11813
12119
|
reopenBacklog: () => reopenBacklog,
|
|
11814
12120
|
patchBacklog: () => patchBacklog,
|
|
11815
12121
|
markForReviewBacklog: () => markForReviewBacklog,
|
|
@@ -11817,24 +12123,47 @@ __export(exports_backlog, {
|
|
|
11817
12123
|
completeBacklog: () => completeBacklog,
|
|
11818
12124
|
addBacklog: () => addBacklog
|
|
11819
12125
|
});
|
|
11820
|
-
async function
|
|
12126
|
+
async function createDefaultDeps10() {
|
|
11821
12127
|
const client2 = await getConvexClient();
|
|
11822
|
-
|
|
12128
|
+
return {
|
|
12129
|
+
backend: {
|
|
12130
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
12131
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
12132
|
+
},
|
|
12133
|
+
session: {
|
|
12134
|
+
getSessionId,
|
|
12135
|
+
getConvexUrl,
|
|
12136
|
+
getOtherSessionUrls
|
|
12137
|
+
}
|
|
12138
|
+
};
|
|
12139
|
+
}
|
|
12140
|
+
function requireAuth2(d) {
|
|
12141
|
+
const sessionId = d.session.getSessionId();
|
|
11823
12142
|
if (!sessionId) {
|
|
11824
12143
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
11825
12144
|
process.exit(1);
|
|
11826
12145
|
}
|
|
12146
|
+
return sessionId;
|
|
12147
|
+
}
|
|
12148
|
+
function validateChatroomId(chatroomId) {
|
|
11827
12149
|
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
11828
12150
|
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
11829
12151
|
process.exit(1);
|
|
11830
12152
|
}
|
|
12153
|
+
}
|
|
12154
|
+
async function listBacklog(chatroomId, options, deps) {
|
|
12155
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12156
|
+
const sessionId = requireAuth2(d);
|
|
12157
|
+
validateChatroomId(chatroomId);
|
|
11831
12158
|
const validStatuses = [
|
|
11832
12159
|
"pending",
|
|
12160
|
+
"acknowledged",
|
|
11833
12161
|
"in_progress",
|
|
11834
12162
|
"queued",
|
|
11835
12163
|
"backlog",
|
|
12164
|
+
"backlog_acknowledged",
|
|
11836
12165
|
"completed",
|
|
11837
|
-
"
|
|
12166
|
+
"closed",
|
|
11838
12167
|
"active",
|
|
11839
12168
|
"pending_review",
|
|
11840
12169
|
"archived",
|
|
@@ -11844,27 +12173,28 @@ async function listBacklog(chatroomId, options) {
|
|
|
11844
12173
|
if (!statusFilter || !validStatuses.includes(statusFilter)) {
|
|
11845
12174
|
console.error(`❌ Invalid or missing status: ${statusFilter || "(none)"}. Must be one of: ${validStatuses.join(", ")}`);
|
|
11846
12175
|
process.exit(1);
|
|
12176
|
+
return;
|
|
11847
12177
|
}
|
|
11848
12178
|
try {
|
|
11849
|
-
const counts = await
|
|
12179
|
+
const counts = await d.backend.query(api.tasks.getTaskCounts, {
|
|
11850
12180
|
sessionId,
|
|
11851
12181
|
chatroomId
|
|
11852
12182
|
});
|
|
11853
12183
|
let tasks;
|
|
11854
12184
|
if (statusFilter === "active") {
|
|
11855
|
-
tasks = await
|
|
12185
|
+
tasks = await d.backend.query(api.tasks.listActiveTasks, {
|
|
11856
12186
|
sessionId,
|
|
11857
12187
|
chatroomId,
|
|
11858
12188
|
limit: options.limit || 100
|
|
11859
12189
|
});
|
|
11860
12190
|
} else if (statusFilter === "archived") {
|
|
11861
|
-
tasks = await
|
|
12191
|
+
tasks = await d.backend.query(api.tasks.listArchivedTasks, {
|
|
11862
12192
|
sessionId,
|
|
11863
12193
|
chatroomId,
|
|
11864
12194
|
limit: options.limit || 100
|
|
11865
12195
|
});
|
|
11866
12196
|
} else {
|
|
11867
|
-
tasks = await
|
|
12197
|
+
tasks = await d.backend.query(api.tasks.listTasks, {
|
|
11868
12198
|
sessionId,
|
|
11869
12199
|
chatroomId,
|
|
11870
12200
|
statusFilter: statusFilter === "all" ? undefined : statusFilter,
|
|
@@ -11937,25 +12267,20 @@ async function listBacklog(chatroomId, options) {
|
|
|
11937
12267
|
} catch (error) {
|
|
11938
12268
|
console.error(`❌ Failed to list tasks: ${error.message}`);
|
|
11939
12269
|
process.exit(1);
|
|
12270
|
+
return;
|
|
11940
12271
|
}
|
|
11941
12272
|
}
|
|
11942
|
-
async function addBacklog(chatroomId, options) {
|
|
11943
|
-
const
|
|
11944
|
-
const sessionId =
|
|
11945
|
-
|
|
11946
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
11947
|
-
process.exit(1);
|
|
11948
|
-
}
|
|
11949
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
11950
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
11951
|
-
process.exit(1);
|
|
11952
|
-
}
|
|
12273
|
+
async function addBacklog(chatroomId, options, deps) {
|
|
12274
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12275
|
+
const sessionId = requireAuth2(d);
|
|
12276
|
+
validateChatroomId(chatroomId);
|
|
11953
12277
|
if (!options.content || options.content.trim().length === 0) {
|
|
11954
12278
|
console.error(`❌ Task content cannot be empty`);
|
|
11955
12279
|
process.exit(1);
|
|
12280
|
+
return;
|
|
11956
12281
|
}
|
|
11957
12282
|
try {
|
|
11958
|
-
const result = await
|
|
12283
|
+
const result = await d.backend.mutation(api.tasks.createTask, {
|
|
11959
12284
|
sessionId,
|
|
11960
12285
|
chatroomId,
|
|
11961
12286
|
content: options.content.trim(),
|
|
@@ -11971,25 +12296,20 @@ async function addBacklog(chatroomId, options) {
|
|
|
11971
12296
|
} catch (error) {
|
|
11972
12297
|
console.error(`❌ Failed to add task: ${error.message}`);
|
|
11973
12298
|
process.exit(1);
|
|
12299
|
+
return;
|
|
11974
12300
|
}
|
|
11975
12301
|
}
|
|
11976
|
-
async function completeBacklog(chatroomId, options) {
|
|
11977
|
-
const
|
|
11978
|
-
const sessionId =
|
|
11979
|
-
|
|
11980
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
11981
|
-
process.exit(1);
|
|
11982
|
-
}
|
|
11983
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
11984
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
11985
|
-
process.exit(1);
|
|
11986
|
-
}
|
|
12302
|
+
async function completeBacklog(chatroomId, options, deps) {
|
|
12303
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12304
|
+
const sessionId = requireAuth2(d);
|
|
12305
|
+
validateChatroomId(chatroomId);
|
|
11987
12306
|
if (!options.taskId || options.taskId.trim().length === 0) {
|
|
11988
12307
|
console.error(`❌ Task ID is required`);
|
|
11989
12308
|
process.exit(1);
|
|
12309
|
+
return;
|
|
11990
12310
|
}
|
|
11991
12311
|
try {
|
|
11992
|
-
const result = await
|
|
12312
|
+
const result = await d.backend.mutation(api.tasks.completeTaskById, {
|
|
11993
12313
|
sessionId,
|
|
11994
12314
|
taskId: options.taskId,
|
|
11995
12315
|
force: options.force
|
|
@@ -12010,25 +12330,20 @@ async function completeBacklog(chatroomId, options) {
|
|
|
12010
12330
|
} catch (error) {
|
|
12011
12331
|
console.error(`❌ Failed to complete task: ${error.message}`);
|
|
12012
12332
|
process.exit(1);
|
|
12333
|
+
return;
|
|
12013
12334
|
}
|
|
12014
12335
|
}
|
|
12015
|
-
async function reopenBacklog(chatroomId, options) {
|
|
12016
|
-
const
|
|
12017
|
-
const sessionId =
|
|
12018
|
-
|
|
12019
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12020
|
-
process.exit(1);
|
|
12021
|
-
}
|
|
12022
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12023
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
12024
|
-
process.exit(1);
|
|
12025
|
-
}
|
|
12336
|
+
async function reopenBacklog(chatroomId, options, deps) {
|
|
12337
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12338
|
+
const sessionId = requireAuth2(d);
|
|
12339
|
+
validateChatroomId(chatroomId);
|
|
12026
12340
|
if (!options.taskId || options.taskId.trim().length === 0) {
|
|
12027
12341
|
console.error(`❌ Task ID is required`);
|
|
12028
12342
|
process.exit(1);
|
|
12343
|
+
return;
|
|
12029
12344
|
}
|
|
12030
12345
|
try {
|
|
12031
|
-
await
|
|
12346
|
+
await d.backend.mutation(api.tasks.reopenBacklogTask, {
|
|
12032
12347
|
sessionId,
|
|
12033
12348
|
taskId: options.taskId
|
|
12034
12349
|
});
|
|
@@ -12042,36 +12357,34 @@ async function reopenBacklog(chatroomId, options) {
|
|
|
12042
12357
|
} catch (error) {
|
|
12043
12358
|
console.error(`❌ Failed to reopen task: ${error.message}`);
|
|
12044
12359
|
process.exit(1);
|
|
12360
|
+
return;
|
|
12045
12361
|
}
|
|
12046
12362
|
}
|
|
12047
|
-
async function patchBacklog(chatroomId, options) {
|
|
12048
|
-
const
|
|
12049
|
-
const sessionId =
|
|
12050
|
-
|
|
12051
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12052
|
-
process.exit(1);
|
|
12053
|
-
}
|
|
12054
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12055
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
12056
|
-
process.exit(1);
|
|
12057
|
-
}
|
|
12363
|
+
async function patchBacklog(chatroomId, options, deps) {
|
|
12364
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12365
|
+
const sessionId = requireAuth2(d);
|
|
12366
|
+
validateChatroomId(chatroomId);
|
|
12058
12367
|
if (!options.taskId || options.taskId.trim().length === 0) {
|
|
12059
12368
|
console.error(`❌ Task ID is required`);
|
|
12060
12369
|
process.exit(1);
|
|
12370
|
+
return;
|
|
12061
12371
|
}
|
|
12062
12372
|
if (options.complexity === undefined && options.value === undefined && options.priority === undefined) {
|
|
12063
12373
|
console.error(`❌ At least one of --complexity, --value, or --priority is required`);
|
|
12064
12374
|
process.exit(1);
|
|
12375
|
+
return;
|
|
12065
12376
|
}
|
|
12066
12377
|
const validComplexity = ["low", "medium", "high"];
|
|
12067
12378
|
if (options.complexity !== undefined && !validComplexity.includes(options.complexity)) {
|
|
12068
12379
|
console.error(`❌ Invalid complexity: ${options.complexity}. Must be one of: ${validComplexity.join(", ")}`);
|
|
12069
12380
|
process.exit(1);
|
|
12381
|
+
return;
|
|
12070
12382
|
}
|
|
12071
12383
|
const validValue = ["low", "medium", "high"];
|
|
12072
12384
|
if (options.value !== undefined && !validValue.includes(options.value)) {
|
|
12073
12385
|
console.error(`❌ Invalid value: ${options.value}. Must be one of: ${validValue.join(", ")}`);
|
|
12074
12386
|
process.exit(1);
|
|
12387
|
+
return;
|
|
12075
12388
|
}
|
|
12076
12389
|
let priorityNum;
|
|
12077
12390
|
if (options.priority !== undefined) {
|
|
@@ -12079,10 +12392,11 @@ async function patchBacklog(chatroomId, options) {
|
|
|
12079
12392
|
if (isNaN(priorityNum)) {
|
|
12080
12393
|
console.error(`❌ Invalid priority: ${options.priority}. Must be a number.`);
|
|
12081
12394
|
process.exit(1);
|
|
12395
|
+
return;
|
|
12082
12396
|
}
|
|
12083
12397
|
}
|
|
12084
12398
|
try {
|
|
12085
|
-
await
|
|
12399
|
+
await d.backend.mutation(api.tasks.patchTask, {
|
|
12086
12400
|
sessionId,
|
|
12087
12401
|
taskId: options.taskId,
|
|
12088
12402
|
complexity: options.complexity,
|
|
@@ -12105,37 +12419,35 @@ async function patchBacklog(chatroomId, options) {
|
|
|
12105
12419
|
} catch (error) {
|
|
12106
12420
|
console.error(`❌ Failed to patch task: ${error.message}`);
|
|
12107
12421
|
process.exit(1);
|
|
12422
|
+
return;
|
|
12108
12423
|
}
|
|
12109
12424
|
}
|
|
12110
|
-
async function scoreBacklog(chatroomId, options) {
|
|
12111
|
-
const
|
|
12112
|
-
const sessionId =
|
|
12113
|
-
|
|
12114
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12115
|
-
process.exit(1);
|
|
12116
|
-
}
|
|
12117
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12118
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
12119
|
-
process.exit(1);
|
|
12120
|
-
}
|
|
12425
|
+
async function scoreBacklog(chatroomId, options, deps) {
|
|
12426
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12427
|
+
const sessionId = requireAuth2(d);
|
|
12428
|
+
validateChatroomId(chatroomId);
|
|
12121
12429
|
if (!options.taskId || options.taskId.trim().length === 0) {
|
|
12122
12430
|
console.error(`❌ Task ID is required`);
|
|
12123
12431
|
process.exit(1);
|
|
12432
|
+
return;
|
|
12124
12433
|
}
|
|
12125
12434
|
if (options.complexity === undefined && options.value === undefined && options.priority === undefined) {
|
|
12126
12435
|
console.error(`❌ At least one of --complexity, --value, or --priority is required`);
|
|
12127
12436
|
console.error(` Example: chatroom backlog score --task-id=... --complexity=medium --value=high`);
|
|
12128
12437
|
process.exit(1);
|
|
12438
|
+
return;
|
|
12129
12439
|
}
|
|
12130
12440
|
const validComplexity = ["low", "medium", "high"];
|
|
12131
12441
|
if (options.complexity !== undefined && !validComplexity.includes(options.complexity)) {
|
|
12132
12442
|
console.error(`❌ Invalid complexity: ${options.complexity}. Must be one of: ${validComplexity.join(", ")}`);
|
|
12133
12443
|
process.exit(1);
|
|
12444
|
+
return;
|
|
12134
12445
|
}
|
|
12135
12446
|
const validValue = ["low", "medium", "high"];
|
|
12136
12447
|
if (options.value !== undefined && !validValue.includes(options.value)) {
|
|
12137
12448
|
console.error(`❌ Invalid value: ${options.value}. Must be one of: ${validValue.join(", ")}`);
|
|
12138
12449
|
process.exit(1);
|
|
12450
|
+
return;
|
|
12139
12451
|
}
|
|
12140
12452
|
let priorityNum;
|
|
12141
12453
|
if (options.priority !== undefined) {
|
|
@@ -12143,10 +12455,11 @@ async function scoreBacklog(chatroomId, options) {
|
|
|
12143
12455
|
if (isNaN(priorityNum)) {
|
|
12144
12456
|
console.error(`❌ Invalid priority: ${options.priority}. Must be a number.`);
|
|
12145
12457
|
process.exit(1);
|
|
12458
|
+
return;
|
|
12146
12459
|
}
|
|
12147
12460
|
}
|
|
12148
12461
|
try {
|
|
12149
|
-
await
|
|
12462
|
+
await d.backend.mutation(api.tasks.patchTask, {
|
|
12150
12463
|
sessionId,
|
|
12151
12464
|
taskId: options.taskId,
|
|
12152
12465
|
complexity: options.complexity,
|
|
@@ -12169,25 +12482,20 @@ async function scoreBacklog(chatroomId, options) {
|
|
|
12169
12482
|
} catch (error) {
|
|
12170
12483
|
console.error(`❌ Failed to score task: ${error.message}`);
|
|
12171
12484
|
process.exit(1);
|
|
12485
|
+
return;
|
|
12172
12486
|
}
|
|
12173
12487
|
}
|
|
12174
|
-
async function markForReviewBacklog(chatroomId, options) {
|
|
12175
|
-
const
|
|
12176
|
-
const sessionId =
|
|
12177
|
-
|
|
12178
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12179
|
-
process.exit(1);
|
|
12180
|
-
}
|
|
12181
|
-
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12182
|
-
console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
|
|
12183
|
-
process.exit(1);
|
|
12184
|
-
}
|
|
12488
|
+
async function markForReviewBacklog(chatroomId, options, deps) {
|
|
12489
|
+
const d = deps ?? await createDefaultDeps10();
|
|
12490
|
+
const sessionId = requireAuth2(d);
|
|
12491
|
+
validateChatroomId(chatroomId);
|
|
12185
12492
|
if (!options.taskId || options.taskId.trim().length === 0) {
|
|
12186
12493
|
console.error(`❌ Task ID is required`);
|
|
12187
12494
|
process.exit(1);
|
|
12495
|
+
return;
|
|
12188
12496
|
}
|
|
12189
12497
|
try {
|
|
12190
|
-
await
|
|
12498
|
+
await d.backend.mutation(api.tasks.markBacklogForReview, {
|
|
12191
12499
|
sessionId,
|
|
12192
12500
|
taskId: options.taskId
|
|
12193
12501
|
});
|
|
@@ -12201,46 +12509,25 @@ async function markForReviewBacklog(chatroomId, options) {
|
|
|
12201
12509
|
} catch (error) {
|
|
12202
12510
|
console.error(`❌ Failed to mark task for review: ${error.message}`);
|
|
12203
12511
|
process.exit(1);
|
|
12204
|
-
|
|
12205
|
-
}
|
|
12206
|
-
async function resetBacklog(chatroomId, options) {
|
|
12207
|
-
const client2 = await getConvexClient();
|
|
12208
|
-
const sessionId = getSessionId();
|
|
12209
|
-
if (!sessionId) {
|
|
12210
|
-
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12211
|
-
process.exit(1);
|
|
12212
|
-
}
|
|
12213
|
-
try {
|
|
12214
|
-
const result = await client2.mutation(api.tasks.resetStuckTask, {
|
|
12215
|
-
sessionId,
|
|
12216
|
-
taskId: options.taskId
|
|
12217
|
-
});
|
|
12218
|
-
console.log("");
|
|
12219
|
-
console.log("✅ Task reset to pending");
|
|
12220
|
-
console.log(` ID: ${options.taskId}`);
|
|
12221
|
-
if (result.previousAssignee) {
|
|
12222
|
-
console.log(` Previous assignee: ${result.previousAssignee}`);
|
|
12223
|
-
}
|
|
12224
|
-
console.log("");
|
|
12225
|
-
} catch (error) {
|
|
12226
|
-
console.error(`❌ Failed to reset task: ${error.message}`);
|
|
12227
|
-
process.exit(1);
|
|
12512
|
+
return;
|
|
12228
12513
|
}
|
|
12229
12514
|
}
|
|
12230
12515
|
function getStatusEmoji(status) {
|
|
12231
12516
|
switch (status) {
|
|
12232
12517
|
case "pending":
|
|
12233
12518
|
return "\uD83D\uDFE2";
|
|
12519
|
+
case "acknowledged":
|
|
12520
|
+
return "\uD83D\uDCEC";
|
|
12234
12521
|
case "in_progress":
|
|
12235
12522
|
return "\uD83D\uDD35";
|
|
12236
12523
|
case "queued":
|
|
12237
12524
|
return "\uD83D\uDFE1";
|
|
12238
12525
|
case "backlog":
|
|
12239
12526
|
return "⚪";
|
|
12527
|
+
case "backlog_acknowledged":
|
|
12528
|
+
return "\uD83D\uDCCB";
|
|
12240
12529
|
case "completed":
|
|
12241
12530
|
return "✅";
|
|
12242
|
-
case "cancelled":
|
|
12243
|
-
return "❌";
|
|
12244
12531
|
case "pending_user_review":
|
|
12245
12532
|
return "\uD83D\uDC40";
|
|
12246
12533
|
case "closed":
|
|
@@ -12284,15 +12571,29 @@ function resolveContent(content, filePath, optionName) {
|
|
|
12284
12571
|
}
|
|
12285
12572
|
var init_file_content = () => {};
|
|
12286
12573
|
|
|
12287
|
-
// src/commands/messages.ts
|
|
12574
|
+
// src/commands/messages/index.ts
|
|
12288
12575
|
var exports_messages = {};
|
|
12289
12576
|
__export(exports_messages, {
|
|
12290
12577
|
listSinceMessage: () => listSinceMessage,
|
|
12291
12578
|
listBySenderRole: () => listBySenderRole
|
|
12292
12579
|
});
|
|
12293
|
-
async function
|
|
12580
|
+
async function createDefaultDeps11() {
|
|
12294
12581
|
const client2 = await getConvexClient();
|
|
12295
|
-
|
|
12582
|
+
return {
|
|
12583
|
+
backend: {
|
|
12584
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
12585
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
12586
|
+
},
|
|
12587
|
+
session: {
|
|
12588
|
+
getSessionId,
|
|
12589
|
+
getConvexUrl,
|
|
12590
|
+
getOtherSessionUrls
|
|
12591
|
+
}
|
|
12592
|
+
};
|
|
12593
|
+
}
|
|
12594
|
+
async function listBySenderRole(chatroomId, options, deps) {
|
|
12595
|
+
const d = deps ?? await createDefaultDeps11();
|
|
12596
|
+
const sessionId = d.session.getSessionId();
|
|
12296
12597
|
if (!sessionId) {
|
|
12297
12598
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12298
12599
|
process.exit(1);
|
|
@@ -12302,7 +12603,7 @@ async function listBySenderRole(chatroomId, options) {
|
|
|
12302
12603
|
process.exit(1);
|
|
12303
12604
|
}
|
|
12304
12605
|
try {
|
|
12305
|
-
const messages = await
|
|
12606
|
+
const messages = await d.backend.query(api.messages.listBySenderRole, {
|
|
12306
12607
|
sessionId,
|
|
12307
12608
|
chatroomId,
|
|
12308
12609
|
senderRole: options.senderRole,
|
|
@@ -12352,11 +12653,12 @@ ${content.split(`
|
|
|
12352
12653
|
console.error(`
|
|
12353
12654
|
❌ Error fetching messages: ${errorMessage}`);
|
|
12354
12655
|
process.exit(1);
|
|
12656
|
+
return;
|
|
12355
12657
|
}
|
|
12356
12658
|
}
|
|
12357
|
-
async function listSinceMessage(chatroomId, options) {
|
|
12358
|
-
const
|
|
12359
|
-
const sessionId = getSessionId();
|
|
12659
|
+
async function listSinceMessage(chatroomId, options, deps) {
|
|
12660
|
+
const d = deps ?? await createDefaultDeps11();
|
|
12661
|
+
const sessionId = d.session.getSessionId();
|
|
12360
12662
|
if (!sessionId) {
|
|
12361
12663
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12362
12664
|
process.exit(1);
|
|
@@ -12366,7 +12668,7 @@ async function listSinceMessage(chatroomId, options) {
|
|
|
12366
12668
|
process.exit(1);
|
|
12367
12669
|
}
|
|
12368
12670
|
try {
|
|
12369
|
-
const messages = await
|
|
12671
|
+
const messages = await d.backend.query(api.messages.listSinceMessage, {
|
|
12370
12672
|
sessionId,
|
|
12371
12673
|
chatroomId,
|
|
12372
12674
|
sinceMessageId: options.sinceMessageId,
|
|
@@ -12414,6 +12716,7 @@ ${content.split(`
|
|
|
12414
12716
|
console.error(`
|
|
12415
12717
|
❌ Error fetching messages: ${errorMessage}`);
|
|
12416
12718
|
process.exit(1);
|
|
12719
|
+
return;
|
|
12417
12720
|
}
|
|
12418
12721
|
}
|
|
12419
12722
|
var init_messages = __esm(() => {
|
|
@@ -12422,7 +12725,7 @@ var init_messages = __esm(() => {
|
|
|
12422
12725
|
init_client2();
|
|
12423
12726
|
});
|
|
12424
12727
|
|
|
12425
|
-
// src/commands/context.ts
|
|
12728
|
+
// src/commands/context/index.ts
|
|
12426
12729
|
var exports_context = {};
|
|
12427
12730
|
__export(exports_context, {
|
|
12428
12731
|
readContext: () => readContext,
|
|
@@ -12430,9 +12733,23 @@ __export(exports_context, {
|
|
|
12430
12733
|
listContexts: () => listContexts,
|
|
12431
12734
|
inspectContext: () => inspectContext
|
|
12432
12735
|
});
|
|
12433
|
-
async function
|
|
12736
|
+
async function createDefaultDeps12() {
|
|
12434
12737
|
const client2 = await getConvexClient();
|
|
12435
|
-
|
|
12738
|
+
return {
|
|
12739
|
+
backend: {
|
|
12740
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
12741
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
12742
|
+
},
|
|
12743
|
+
session: {
|
|
12744
|
+
getSessionId,
|
|
12745
|
+
getConvexUrl,
|
|
12746
|
+
getOtherSessionUrls
|
|
12747
|
+
}
|
|
12748
|
+
};
|
|
12749
|
+
}
|
|
12750
|
+
async function readContext(chatroomId, options, deps) {
|
|
12751
|
+
const d = deps ?? await createDefaultDeps12();
|
|
12752
|
+
const sessionId = d.session.getSessionId();
|
|
12436
12753
|
if (!sessionId) {
|
|
12437
12754
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12438
12755
|
process.exit(1);
|
|
@@ -12442,12 +12759,12 @@ async function readContext(chatroomId, options) {
|
|
|
12442
12759
|
process.exit(1);
|
|
12443
12760
|
}
|
|
12444
12761
|
try {
|
|
12445
|
-
const context = await
|
|
12762
|
+
const context = await d.backend.query(api.messages.getContextForRole, {
|
|
12446
12763
|
sessionId,
|
|
12447
12764
|
chatroomId,
|
|
12448
12765
|
role: options.role
|
|
12449
12766
|
});
|
|
12450
|
-
if (context.messages.length === 0) {
|
|
12767
|
+
if (context.messages.length === 0 && !context.currentContext) {
|
|
12451
12768
|
console.log(`
|
|
12452
12769
|
\uD83D\uDCED No context available`);
|
|
12453
12770
|
return;
|
|
@@ -12455,6 +12772,18 @@ async function readContext(chatroomId, options) {
|
|
|
12455
12772
|
console.log(`
|
|
12456
12773
|
\uD83D\uDCDA CONTEXT FOR ${options.role.toUpperCase()}`);
|
|
12457
12774
|
console.log("═".repeat(60));
|
|
12775
|
+
if (context.currentContext) {
|
|
12776
|
+
console.log(`
|
|
12777
|
+
\uD83D\uDCCC Current Context:`);
|
|
12778
|
+
console.log(` Created by: ${context.currentContext.createdBy}`);
|
|
12779
|
+
console.log(` Created at: ${new Date(context.currentContext.createdAt).toLocaleString()}`);
|
|
12780
|
+
console.log(` Content:`);
|
|
12781
|
+
const safeContextContent = sanitizeForTerminal(context.currentContext.content);
|
|
12782
|
+
console.log(safeContextContent.split(`
|
|
12783
|
+
`).map((l) => ` ${l}`).join(`
|
|
12784
|
+
`));
|
|
12785
|
+
console.log("─".repeat(60));
|
|
12786
|
+
}
|
|
12458
12787
|
if (context.originMessage) {
|
|
12459
12788
|
console.log(`
|
|
12460
12789
|
\uD83C\uDFAF Origin Message:`);
|
|
@@ -12481,9 +12810,12 @@ async function readContext(chatroomId, options) {
|
|
|
12481
12810
|
\uD83D\uDD39 Message ID: ${message._id}`);
|
|
12482
12811
|
console.log(` Time: ${timestamp}`);
|
|
12483
12812
|
console.log(` From: ${message.senderRole}`);
|
|
12813
|
+
if (message.targetRole) {
|
|
12814
|
+
console.log(` To: ${message.targetRole}`);
|
|
12815
|
+
}
|
|
12484
12816
|
console.log(` Type: ${message.type}${classificationBadge}`);
|
|
12485
12817
|
if (message.featureTitle) {
|
|
12486
|
-
console.log(` Feature: ${message.featureTitle}`);
|
|
12818
|
+
console.log(` Feature: ${sanitizeForTerminal(message.featureTitle)}`);
|
|
12487
12819
|
}
|
|
12488
12820
|
if (message.taskId) {
|
|
12489
12821
|
console.log(` Task:`);
|
|
@@ -12492,7 +12824,8 @@ async function readContext(chatroomId, options) {
|
|
|
12492
12824
|
console.log(` Status: ${message.taskStatus}`);
|
|
12493
12825
|
}
|
|
12494
12826
|
if (message.taskContent) {
|
|
12495
|
-
|
|
12827
|
+
const safeTaskContent = sanitizeForTerminal(message.taskContent);
|
|
12828
|
+
console.log(` Content: ${safeTaskContent.split(`
|
|
12496
12829
|
`).map((l, i2) => i2 === 0 ? l : ` ${l}`).join(`
|
|
12497
12830
|
`)}`);
|
|
12498
12831
|
}
|
|
@@ -12502,7 +12835,7 @@ async function readContext(chatroomId, options) {
|
|
|
12502
12835
|
for (const task of message.attachedTasks) {
|
|
12503
12836
|
console.log(` \uD83D\uDD39 Task ID: ${task._id}`);
|
|
12504
12837
|
console.log(` Type: Task`);
|
|
12505
|
-
const contentLines = task.content.split(`
|
|
12838
|
+
const contentLines = sanitizeForTerminal(task.content).split(`
|
|
12506
12839
|
`);
|
|
12507
12840
|
console.log(` Content: ${contentLines[0]}`);
|
|
12508
12841
|
if (contentLines.length > 1) {
|
|
@@ -12513,20 +12846,22 @@ async function readContext(chatroomId, options) {
|
|
|
12513
12846
|
}
|
|
12514
12847
|
}
|
|
12515
12848
|
console.log(` Content:`);
|
|
12516
|
-
|
|
12849
|
+
const safeMessageContent = sanitizeForTerminal(message.content);
|
|
12850
|
+
console.log(safeMessageContent.split(`
|
|
12517
12851
|
`).map((l) => ` ${l}`).join(`
|
|
12518
12852
|
`));
|
|
12519
12853
|
}
|
|
12520
12854
|
console.log(`
|
|
12521
12855
|
` + "═".repeat(60));
|
|
12522
12856
|
} catch (err) {
|
|
12523
|
-
console.error(`❌ Failed to read context: ${err.message}`);
|
|
12857
|
+
console.error(`❌ Failed to read context: ${sanitizeUnknownForTerminal(err.message)}`);
|
|
12524
12858
|
process.exit(1);
|
|
12859
|
+
return;
|
|
12525
12860
|
}
|
|
12526
12861
|
}
|
|
12527
|
-
async function newContext(chatroomId, options) {
|
|
12528
|
-
const
|
|
12529
|
-
const sessionId = getSessionId();
|
|
12862
|
+
async function newContext(chatroomId, options, deps) {
|
|
12863
|
+
const d = deps ?? await createDefaultDeps12();
|
|
12864
|
+
const sessionId = d.session.getSessionId();
|
|
12530
12865
|
if (!sessionId) {
|
|
12531
12866
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12532
12867
|
process.exit(1);
|
|
@@ -12540,11 +12875,12 @@ async function newContext(chatroomId, options) {
|
|
|
12540
12875
|
process.exit(1);
|
|
12541
12876
|
}
|
|
12542
12877
|
try {
|
|
12543
|
-
const contextId = await
|
|
12878
|
+
const contextId = await d.backend.mutation(api.contexts.createContext, {
|
|
12544
12879
|
sessionId,
|
|
12545
12880
|
chatroomId,
|
|
12546
12881
|
content: options.content,
|
|
12547
|
-
role: options.role
|
|
12882
|
+
role: options.role,
|
|
12883
|
+
triggerMessageId: options.triggerMessageId
|
|
12548
12884
|
});
|
|
12549
12885
|
console.log(`✅ Context created successfully`);
|
|
12550
12886
|
console.log(` Context ID: ${contextId}`);
|
|
@@ -12554,11 +12890,12 @@ async function newContext(chatroomId, options) {
|
|
|
12554
12890
|
} catch (err) {
|
|
12555
12891
|
console.error(`❌ Failed to create context: ${err.message}`);
|
|
12556
12892
|
process.exit(1);
|
|
12893
|
+
return;
|
|
12557
12894
|
}
|
|
12558
12895
|
}
|
|
12559
|
-
async function listContexts(chatroomId, options) {
|
|
12560
|
-
const
|
|
12561
|
-
const sessionId = getSessionId();
|
|
12896
|
+
async function listContexts(chatroomId, options, deps) {
|
|
12897
|
+
const d = deps ?? await createDefaultDeps12();
|
|
12898
|
+
const sessionId = d.session.getSessionId();
|
|
12562
12899
|
if (!sessionId) {
|
|
12563
12900
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12564
12901
|
process.exit(1);
|
|
@@ -12568,7 +12905,7 @@ async function listContexts(chatroomId, options) {
|
|
|
12568
12905
|
process.exit(1);
|
|
12569
12906
|
}
|
|
12570
12907
|
try {
|
|
12571
|
-
const contexts = await
|
|
12908
|
+
const contexts = await d.backend.query(api.contexts.listContexts, {
|
|
12572
12909
|
sessionId,
|
|
12573
12910
|
chatroomId,
|
|
12574
12911
|
limit: options.limit ?? 10
|
|
@@ -12594,7 +12931,8 @@ async function listContexts(chatroomId, options) {
|
|
|
12594
12931
|
console.log(` Messages at creation: ${context.messageCountAtCreation}`);
|
|
12595
12932
|
}
|
|
12596
12933
|
console.log(` Content:`);
|
|
12597
|
-
const
|
|
12934
|
+
const safeContent = sanitizeForTerminal(context.content);
|
|
12935
|
+
const truncatedContent = safeContent.length > 200 ? safeContent.slice(0, 200) + "..." : safeContent;
|
|
12598
12936
|
console.log(truncatedContent.split(`
|
|
12599
12937
|
`).map((l) => ` ${l}`).join(`
|
|
12600
12938
|
`));
|
|
@@ -12602,19 +12940,20 @@ async function listContexts(chatroomId, options) {
|
|
|
12602
12940
|
console.log(`
|
|
12603
12941
|
` + "═".repeat(60));
|
|
12604
12942
|
} catch (err) {
|
|
12605
|
-
console.error(`❌ Failed to list contexts: ${err.message}`);
|
|
12943
|
+
console.error(`❌ Failed to list contexts: ${sanitizeUnknownForTerminal(err.message)}`);
|
|
12606
12944
|
process.exit(1);
|
|
12945
|
+
return;
|
|
12607
12946
|
}
|
|
12608
12947
|
}
|
|
12609
|
-
async function inspectContext(chatroomId, options) {
|
|
12610
|
-
const
|
|
12611
|
-
const sessionId = getSessionId();
|
|
12948
|
+
async function inspectContext(chatroomId, options, deps) {
|
|
12949
|
+
const d = deps ?? await createDefaultDeps12();
|
|
12950
|
+
const sessionId = d.session.getSessionId();
|
|
12612
12951
|
if (!sessionId) {
|
|
12613
12952
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12614
12953
|
process.exit(1);
|
|
12615
12954
|
}
|
|
12616
12955
|
try {
|
|
12617
|
-
const context = await
|
|
12956
|
+
const context = await d.backend.query(api.contexts.getContext, {
|
|
12618
12957
|
sessionId,
|
|
12619
12958
|
contextId: options.contextId
|
|
12620
12959
|
});
|
|
@@ -12642,7 +12981,7 @@ async function inspectContext(chatroomId, options) {
|
|
|
12642
12981
|
console.log(`
|
|
12643
12982
|
\uD83D\uDCDD Content:`);
|
|
12644
12983
|
console.log("─".repeat(60));
|
|
12645
|
-
console.log(context.content);
|
|
12984
|
+
console.log(sanitizeForTerminal(context.content));
|
|
12646
12985
|
console.log("─".repeat(60));
|
|
12647
12986
|
console.log(`
|
|
12648
12987
|
\uD83D\uDCA1 To create a new context:`);
|
|
@@ -12650,8 +12989,9 @@ async function inspectContext(chatroomId, options) {
|
|
|
12650
12989
|
console.log(`
|
|
12651
12990
|
` + "═".repeat(60));
|
|
12652
12991
|
} catch (err) {
|
|
12653
|
-
console.error(`❌ Failed to inspect context: ${err.message}`);
|
|
12992
|
+
console.error(`❌ Failed to inspect context: ${sanitizeUnknownForTerminal(err.message)}`);
|
|
12654
12993
|
process.exit(1);
|
|
12994
|
+
return;
|
|
12655
12995
|
}
|
|
12656
12996
|
}
|
|
12657
12997
|
var init_context = __esm(() => {
|
|
@@ -12660,27 +13000,43 @@ var init_context = __esm(() => {
|
|
|
12660
13000
|
init_client2();
|
|
12661
13001
|
});
|
|
12662
13002
|
|
|
12663
|
-
// src/commands/guidelines.ts
|
|
13003
|
+
// src/commands/guidelines/index.ts
|
|
12664
13004
|
var exports_guidelines = {};
|
|
12665
13005
|
__export(exports_guidelines, {
|
|
12666
13006
|
viewGuidelines: () => viewGuidelines,
|
|
12667
13007
|
listGuidelineTypes: () => listGuidelineTypes
|
|
12668
13008
|
});
|
|
12669
|
-
async function
|
|
13009
|
+
async function createDefaultDeps13() {
|
|
13010
|
+
const client2 = await getConvexClient();
|
|
13011
|
+
return {
|
|
13012
|
+
backend: {
|
|
13013
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
13014
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
13015
|
+
},
|
|
13016
|
+
session: {
|
|
13017
|
+
getSessionId,
|
|
13018
|
+
getConvexUrl,
|
|
13019
|
+
getOtherSessionUrls
|
|
13020
|
+
}
|
|
13021
|
+
};
|
|
13022
|
+
}
|
|
13023
|
+
async function viewGuidelines(options, deps) {
|
|
13024
|
+
const d = deps ?? await createDefaultDeps13();
|
|
12670
13025
|
const { type } = options;
|
|
12671
13026
|
if (!VALID_TYPES.includes(type)) {
|
|
12672
13027
|
console.error(`❌ Invalid guideline type: "${type}"`);
|
|
12673
13028
|
console.error(` Valid types: ${VALID_TYPES.join(", ")}`);
|
|
12674
13029
|
process.exit(1);
|
|
13030
|
+
return;
|
|
12675
13031
|
}
|
|
12676
|
-
const sessionId = getSessionId();
|
|
13032
|
+
const sessionId = d.session.getSessionId();
|
|
12677
13033
|
if (!sessionId) {
|
|
12678
13034
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12679
13035
|
process.exit(1);
|
|
13036
|
+
return;
|
|
12680
13037
|
}
|
|
12681
|
-
const client2 = await getConvexClient();
|
|
12682
13038
|
try {
|
|
12683
|
-
const result = await
|
|
13039
|
+
const result = await d.backend.query(api.guidelines.getGuidelines, {
|
|
12684
13040
|
type
|
|
12685
13041
|
});
|
|
12686
13042
|
console.log(`
|
|
@@ -12696,17 +13052,19 @@ ${"═".repeat(60)}
|
|
|
12696
13052
|
const err = error;
|
|
12697
13053
|
console.error(`❌ Error fetching guidelines: ${err.message}`);
|
|
12698
13054
|
process.exit(1);
|
|
13055
|
+
return;
|
|
12699
13056
|
}
|
|
12700
13057
|
}
|
|
12701
|
-
async function listGuidelineTypes() {
|
|
12702
|
-
const
|
|
13058
|
+
async function listGuidelineTypes(deps) {
|
|
13059
|
+
const d = deps ?? await createDefaultDeps13();
|
|
13060
|
+
const sessionId = d.session.getSessionId();
|
|
12703
13061
|
if (!sessionId) {
|
|
12704
13062
|
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
12705
13063
|
process.exit(1);
|
|
13064
|
+
return;
|
|
12706
13065
|
}
|
|
12707
|
-
const client2 = await getConvexClient();
|
|
12708
13066
|
try {
|
|
12709
|
-
const types2 = await
|
|
13067
|
+
const types2 = await d.backend.query(api.guidelines.listGuidelineTypes, {});
|
|
12710
13068
|
console.log(`
|
|
12711
13069
|
\uD83D\uDCCB Available Guideline Types
|
|
12712
13070
|
`);
|
|
@@ -12722,6 +13080,7 @@ Usage: chatroom guidelines view --type=<type>
|
|
|
12722
13080
|
const err = error;
|
|
12723
13081
|
console.error(`❌ Error fetching guideline types: ${err.message}`);
|
|
12724
13082
|
process.exit(1);
|
|
13083
|
+
return;
|
|
12725
13084
|
}
|
|
12726
13085
|
}
|
|
12727
13086
|
var VALID_TYPES;
|
|
@@ -12732,26 +13091,44 @@ var init_guidelines = __esm(() => {
|
|
|
12732
13091
|
VALID_TYPES = ["coding", "security", "design", "performance", "all"];
|
|
12733
13092
|
});
|
|
12734
13093
|
|
|
12735
|
-
// src/commands/artifact.ts
|
|
13094
|
+
// src/commands/artifact/index.ts
|
|
12736
13095
|
var exports_artifact = {};
|
|
12737
13096
|
__export(exports_artifact, {
|
|
12738
13097
|
viewManyArtifacts: () => viewManyArtifacts,
|
|
12739
13098
|
viewArtifact: () => viewArtifact,
|
|
12740
13099
|
createArtifact: () => createArtifact
|
|
12741
13100
|
});
|
|
12742
|
-
async function
|
|
12743
|
-
const
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
13101
|
+
async function createDefaultDeps14() {
|
|
13102
|
+
const client2 = await getConvexClient();
|
|
13103
|
+
return {
|
|
13104
|
+
backend: {
|
|
13105
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
13106
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
13107
|
+
},
|
|
13108
|
+
session: {
|
|
13109
|
+
getSessionId,
|
|
13110
|
+
getConvexUrl,
|
|
13111
|
+
getOtherSessionUrls
|
|
13112
|
+
}
|
|
13113
|
+
};
|
|
13114
|
+
}
|
|
13115
|
+
async function createArtifact(chatroomId, options, deps) {
|
|
13116
|
+
const d = deps ?? await createDefaultDeps14();
|
|
13117
|
+
const sessionId = d.session.getSessionId();
|
|
13118
|
+
if (!sessionId) {
|
|
13119
|
+
formatAuthError(d.session.getConvexUrl(), d.session.getOtherSessionUrls());
|
|
13120
|
+
process.exit(1);
|
|
13121
|
+
return;
|
|
13122
|
+
}
|
|
12748
13123
|
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12749
13124
|
formatChatroomIdError(chatroomId);
|
|
12750
13125
|
process.exit(1);
|
|
13126
|
+
return;
|
|
12751
13127
|
}
|
|
12752
13128
|
if (!options.fromFile.endsWith(".md")) {
|
|
12753
13129
|
formatValidationError("file extension", options.fromFile, "*.md");
|
|
12754
13130
|
process.exit(1);
|
|
13131
|
+
return;
|
|
12755
13132
|
}
|
|
12756
13133
|
let content;
|
|
12757
13134
|
try {
|
|
@@ -12759,14 +13136,15 @@ async function createArtifact(chatroomId, options) {
|
|
|
12759
13136
|
} catch (err) {
|
|
12760
13137
|
formatFileError("read for --from-file", options.fromFile, err.message);
|
|
12761
13138
|
process.exit(1);
|
|
13139
|
+
return;
|
|
12762
13140
|
}
|
|
12763
13141
|
if (!content || content.trim().length === 0) {
|
|
12764
13142
|
formatError("File is empty");
|
|
12765
13143
|
process.exit(1);
|
|
13144
|
+
return;
|
|
12766
13145
|
}
|
|
12767
|
-
const client2 = await getConvexClient();
|
|
12768
13146
|
try {
|
|
12769
|
-
const artifactId = await
|
|
13147
|
+
const artifactId = await d.backend.mutation(api.artifacts.create, {
|
|
12770
13148
|
sessionId,
|
|
12771
13149
|
chatroomId,
|
|
12772
13150
|
filename: options.filename,
|
|
@@ -12785,21 +13163,24 @@ async function createArtifact(chatroomId, options) {
|
|
|
12785
13163
|
} catch (error) {
|
|
12786
13164
|
formatError("Failed to create artifact", [String(error)]);
|
|
12787
13165
|
process.exit(1);
|
|
13166
|
+
return;
|
|
12788
13167
|
}
|
|
12789
13168
|
}
|
|
12790
|
-
async function viewArtifact(chatroomId, options) {
|
|
12791
|
-
const
|
|
13169
|
+
async function viewArtifact(chatroomId, options, deps) {
|
|
13170
|
+
const d = deps ?? await createDefaultDeps14();
|
|
13171
|
+
const sessionId = d.session.getSessionId();
|
|
12792
13172
|
if (!sessionId) {
|
|
12793
|
-
formatAuthError();
|
|
13173
|
+
formatAuthError(d.session.getConvexUrl(), d.session.getOtherSessionUrls());
|
|
12794
13174
|
process.exit(1);
|
|
13175
|
+
return;
|
|
12795
13176
|
}
|
|
12796
13177
|
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12797
13178
|
formatChatroomIdError(chatroomId);
|
|
12798
13179
|
process.exit(1);
|
|
13180
|
+
return;
|
|
12799
13181
|
}
|
|
12800
|
-
const client2 = await getConvexClient();
|
|
12801
13182
|
try {
|
|
12802
|
-
const artifact = await
|
|
13183
|
+
const artifact = await d.backend.query(api.artifacts.get, {
|
|
12803
13184
|
sessionId,
|
|
12804
13185
|
artifactId: options.artifactId
|
|
12805
13186
|
});
|
|
@@ -12810,6 +13191,7 @@ async function viewArtifact(chatroomId, options) {
|
|
|
12810
13191
|
`chatroom artifact create ${chatroomId} --from-file=... --filename=...`
|
|
12811
13192
|
]);
|
|
12812
13193
|
process.exit(1);
|
|
13194
|
+
return;
|
|
12813
13195
|
}
|
|
12814
13196
|
console.log(`\uD83D\uDCC4 Artifact: ${artifact.filename}`);
|
|
12815
13197
|
console.log(`\uD83C\uDD94 ID: ${artifact._id}`);
|
|
@@ -12825,33 +13207,38 @@ async function viewArtifact(chatroomId, options) {
|
|
|
12825
13207
|
} catch (error) {
|
|
12826
13208
|
formatError("Failed to view artifact", [String(error)]);
|
|
12827
13209
|
process.exit(1);
|
|
13210
|
+
return;
|
|
12828
13211
|
}
|
|
12829
13212
|
}
|
|
12830
|
-
async function viewManyArtifacts(chatroomId, options) {
|
|
12831
|
-
const
|
|
13213
|
+
async function viewManyArtifacts(chatroomId, options, deps) {
|
|
13214
|
+
const d = deps ?? await createDefaultDeps14();
|
|
13215
|
+
const sessionId = d.session.getSessionId();
|
|
12832
13216
|
if (!sessionId) {
|
|
12833
|
-
formatAuthError();
|
|
13217
|
+
formatAuthError(d.session.getConvexUrl(), d.session.getOtherSessionUrls());
|
|
12834
13218
|
process.exit(1);
|
|
13219
|
+
return;
|
|
12835
13220
|
}
|
|
12836
13221
|
if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
|
|
12837
13222
|
formatChatroomIdError(chatroomId);
|
|
12838
13223
|
process.exit(1);
|
|
13224
|
+
return;
|
|
12839
13225
|
}
|
|
12840
13226
|
if (options.artifactIds.length === 0) {
|
|
12841
13227
|
formatError("No artifact IDs provided", [
|
|
12842
13228
|
"Usage: chatroom artifact view-many <chatroomId> --artifact=id1 --artifact=id2"
|
|
12843
13229
|
]);
|
|
12844
13230
|
process.exit(1);
|
|
13231
|
+
return;
|
|
12845
13232
|
}
|
|
12846
|
-
const client2 = await getConvexClient();
|
|
12847
13233
|
try {
|
|
12848
|
-
const artifacts = await
|
|
13234
|
+
const artifacts = await d.backend.query(api.artifacts.getMany, {
|
|
12849
13235
|
sessionId,
|
|
12850
13236
|
artifactIds: options.artifactIds
|
|
12851
13237
|
});
|
|
12852
13238
|
if (artifacts.length === 0) {
|
|
12853
13239
|
formatError("No artifacts found");
|
|
12854
13240
|
process.exit(1);
|
|
13241
|
+
return;
|
|
12855
13242
|
}
|
|
12856
13243
|
artifacts.forEach((artifact, index) => {
|
|
12857
13244
|
if (index > 0) {
|
|
@@ -12874,20 +13261,248 @@ async function viewManyArtifacts(chatroomId, options) {
|
|
|
12874
13261
|
} catch (error) {
|
|
12875
13262
|
formatError("Failed to view artifacts", [String(error)]);
|
|
12876
13263
|
process.exit(1);
|
|
13264
|
+
return;
|
|
12877
13265
|
}
|
|
12878
13266
|
}
|
|
12879
13267
|
var init_artifact = __esm(() => {
|
|
12880
13268
|
init_api3();
|
|
12881
13269
|
init_storage();
|
|
12882
13270
|
init_client2();
|
|
13271
|
+
init_error_formatting();
|
|
12883
13272
|
init_file_content();
|
|
12884
13273
|
});
|
|
12885
13274
|
|
|
13275
|
+
// src/commands/get-system-prompt/index.ts
|
|
13276
|
+
var exports_get_system_prompt = {};
|
|
13277
|
+
__export(exports_get_system_prompt, {
|
|
13278
|
+
getSystemPrompt: () => getSystemPrompt
|
|
13279
|
+
});
|
|
13280
|
+
async function createDefaultDeps15() {
|
|
13281
|
+
const client2 = await getConvexClient();
|
|
13282
|
+
return {
|
|
13283
|
+
backend: {
|
|
13284
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
13285
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
13286
|
+
},
|
|
13287
|
+
session: {
|
|
13288
|
+
getSessionId,
|
|
13289
|
+
getConvexUrl,
|
|
13290
|
+
getOtherSessionUrls
|
|
13291
|
+
}
|
|
13292
|
+
};
|
|
13293
|
+
}
|
|
13294
|
+
async function getSystemPrompt(chatroomId, options, deps) {
|
|
13295
|
+
const d = deps ?? await createDefaultDeps15();
|
|
13296
|
+
const { role } = options;
|
|
13297
|
+
const sessionId = d.session.getSessionId();
|
|
13298
|
+
if (!sessionId) {
|
|
13299
|
+
console.error(`❌ Not authenticated. Please run: chatroom auth login`);
|
|
13300
|
+
process.exit(1);
|
|
13301
|
+
return;
|
|
13302
|
+
}
|
|
13303
|
+
const convexUrl = d.session.getConvexUrl();
|
|
13304
|
+
try {
|
|
13305
|
+
const chatroom = await d.backend.query(api.chatrooms.get, {
|
|
13306
|
+
sessionId,
|
|
13307
|
+
chatroomId
|
|
13308
|
+
});
|
|
13309
|
+
if (!chatroom) {
|
|
13310
|
+
console.error(`❌ Chatroom not found: ${chatroomId}`);
|
|
13311
|
+
process.exit(1);
|
|
13312
|
+
return;
|
|
13313
|
+
}
|
|
13314
|
+
const prompt = await d.backend.query(api.prompts.webapp.getAgentPrompt, {
|
|
13315
|
+
chatroomId,
|
|
13316
|
+
role,
|
|
13317
|
+
teamName: chatroom.teamName,
|
|
13318
|
+
teamRoles: chatroom.teamRoles,
|
|
13319
|
+
teamEntryPoint: chatroom.teamEntryPoint,
|
|
13320
|
+
convexUrl: convexUrl ?? undefined
|
|
13321
|
+
});
|
|
13322
|
+
console.log(prompt);
|
|
13323
|
+
} catch (error) {
|
|
13324
|
+
const err = error;
|
|
13325
|
+
console.error(`❌ Error fetching system prompt: ${err.message}`);
|
|
13326
|
+
process.exit(1);
|
|
13327
|
+
return;
|
|
13328
|
+
}
|
|
13329
|
+
}
|
|
13330
|
+
var init_get_system_prompt = __esm(() => {
|
|
13331
|
+
init_api3();
|
|
13332
|
+
init_storage();
|
|
13333
|
+
init_client2();
|
|
13334
|
+
});
|
|
13335
|
+
|
|
13336
|
+
// ../../services/backend/config/reliability.ts
|
|
13337
|
+
var DAEMON_HEARTBEAT_INTERVAL_MS = 30000;
|
|
13338
|
+
|
|
13339
|
+
// src/commands/machine/daemon-start/utils.ts
|
|
13340
|
+
function formatTimestamp() {
|
|
13341
|
+
return new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
13342
|
+
}
|
|
13343
|
+
function parseMachineCommand(raw) {
|
|
13344
|
+
switch (raw.type) {
|
|
13345
|
+
case "ping":
|
|
13346
|
+
return { _id: raw._id, type: "ping", payload: {}, createdAt: raw.createdAt };
|
|
13347
|
+
case "status":
|
|
13348
|
+
return { _id: raw._id, type: "status", payload: {}, createdAt: raw.createdAt };
|
|
13349
|
+
case "start-agent": {
|
|
13350
|
+
const { chatroomId, role, agentHarness } = raw.payload;
|
|
13351
|
+
if (!chatroomId || !role || !agentHarness) {
|
|
13352
|
+
console.error(` ⚠️ Invalid start-agent command: missing chatroomId, role, or agentHarness`);
|
|
13353
|
+
return null;
|
|
13354
|
+
}
|
|
13355
|
+
return {
|
|
13356
|
+
_id: raw._id,
|
|
13357
|
+
type: "start-agent",
|
|
13358
|
+
payload: {
|
|
13359
|
+
chatroomId,
|
|
13360
|
+
role,
|
|
13361
|
+
agentHarness,
|
|
13362
|
+
model: raw.payload.model,
|
|
13363
|
+
workingDir: raw.payload.workingDir
|
|
13364
|
+
},
|
|
13365
|
+
createdAt: raw.createdAt
|
|
13366
|
+
};
|
|
13367
|
+
}
|
|
13368
|
+
case "stop-agent": {
|
|
13369
|
+
const { chatroomId, role } = raw.payload;
|
|
13370
|
+
if (!chatroomId || !role) {
|
|
13371
|
+
console.error(` ⚠️ Invalid stop-agent command: missing chatroomId or role`);
|
|
13372
|
+
return null;
|
|
13373
|
+
}
|
|
13374
|
+
return {
|
|
13375
|
+
_id: raw._id,
|
|
13376
|
+
type: "stop-agent",
|
|
13377
|
+
payload: { chatroomId, role },
|
|
13378
|
+
createdAt: raw.createdAt
|
|
13379
|
+
};
|
|
13380
|
+
}
|
|
13381
|
+
default:
|
|
13382
|
+
return null;
|
|
13383
|
+
}
|
|
13384
|
+
}
|
|
13385
|
+
|
|
13386
|
+
// src/commands/machine/events/on-agent-shutdown/index.ts
|
|
13387
|
+
async function onAgentShutdown(ctx, options) {
|
|
13388
|
+
const { chatroomId, role, pid, skipKill } = options;
|
|
13389
|
+
let killed = false;
|
|
13390
|
+
if (!skipKill) {
|
|
13391
|
+
try {
|
|
13392
|
+
ctx.deps.processes.kill(-pid, "SIGTERM");
|
|
13393
|
+
} catch {
|
|
13394
|
+
killed = true;
|
|
13395
|
+
}
|
|
13396
|
+
if (!killed) {
|
|
13397
|
+
const SIGTERM_TIMEOUT_MS = 1e4;
|
|
13398
|
+
const POLL_INTERVAL_MS2 = 500;
|
|
13399
|
+
const deadline = Date.now() + SIGTERM_TIMEOUT_MS;
|
|
13400
|
+
while (Date.now() < deadline) {
|
|
13401
|
+
await ctx.deps.clock.delay(POLL_INTERVAL_MS2);
|
|
13402
|
+
try {
|
|
13403
|
+
ctx.deps.processes.kill(pid, 0);
|
|
13404
|
+
} catch {
|
|
13405
|
+
killed = true;
|
|
13406
|
+
break;
|
|
13407
|
+
}
|
|
13408
|
+
}
|
|
13409
|
+
}
|
|
13410
|
+
if (!killed) {
|
|
13411
|
+
try {
|
|
13412
|
+
ctx.deps.processes.kill(-pid, "SIGKILL");
|
|
13413
|
+
} catch {
|
|
13414
|
+
killed = true;
|
|
13415
|
+
}
|
|
13416
|
+
}
|
|
13417
|
+
if (!killed) {
|
|
13418
|
+
await ctx.deps.clock.delay(5000);
|
|
13419
|
+
try {
|
|
13420
|
+
ctx.deps.processes.kill(pid, 0);
|
|
13421
|
+
console.log(` ⚠️ Process ${pid} (${role}) still alive after SIGKILL — possible zombie`);
|
|
13422
|
+
} catch {
|
|
13423
|
+
killed = true;
|
|
13424
|
+
}
|
|
13425
|
+
}
|
|
13426
|
+
}
|
|
13427
|
+
ctx.deps.stops.mark(chatroomId, role);
|
|
13428
|
+
ctx.deps.machine.clearAgentPid(ctx.machineId, chatroomId, role);
|
|
13429
|
+
let spawnedAgentCleared = false;
|
|
13430
|
+
try {
|
|
13431
|
+
await ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
13432
|
+
sessionId: ctx.sessionId,
|
|
13433
|
+
machineId: ctx.machineId,
|
|
13434
|
+
chatroomId,
|
|
13435
|
+
role,
|
|
13436
|
+
pid: undefined
|
|
13437
|
+
});
|
|
13438
|
+
spawnedAgentCleared = true;
|
|
13439
|
+
} catch (e) {
|
|
13440
|
+
console.log(` ⚠️ Failed to clear spawnedAgent for ${role}: ${e.message}`);
|
|
13441
|
+
}
|
|
13442
|
+
let participantRemoved = false;
|
|
13443
|
+
try {
|
|
13444
|
+
await ctx.deps.backend.mutation(api.participants.leave, {
|
|
13445
|
+
sessionId: ctx.sessionId,
|
|
13446
|
+
chatroomId,
|
|
13447
|
+
role
|
|
13448
|
+
});
|
|
13449
|
+
participantRemoved = true;
|
|
13450
|
+
} catch (e) {
|
|
13451
|
+
console.log(` ⚠️ Failed to remove participant for ${role}: ${e.message}`);
|
|
13452
|
+
}
|
|
13453
|
+
return { killed, cleaned: spawnedAgentCleared && participantRemoved };
|
|
13454
|
+
}
|
|
13455
|
+
var init_on_agent_shutdown = __esm(() => {
|
|
13456
|
+
init_api3();
|
|
13457
|
+
});
|
|
13458
|
+
|
|
13459
|
+
// src/commands/machine/events/on-daemon-shutdown/index.ts
|
|
13460
|
+
async function onDaemonShutdown(ctx) {
|
|
13461
|
+
const agents = ctx.deps.machine.listAgentEntries(ctx.machineId);
|
|
13462
|
+
if (agents.length > 0) {
|
|
13463
|
+
console.log(`[${formatTimestamp()}] Stopping ${agents.length} agent(s)...`);
|
|
13464
|
+
await Promise.allSettled(agents.map(async ({ chatroomId, role, entry }) => {
|
|
13465
|
+
const result = await onAgentShutdown(ctx, {
|
|
13466
|
+
chatroomId,
|
|
13467
|
+
role,
|
|
13468
|
+
pid: entry.pid
|
|
13469
|
+
});
|
|
13470
|
+
if (result.killed) {
|
|
13471
|
+
console.log(` Sent SIGTERM to ${role} (PID ${entry.pid})`);
|
|
13472
|
+
} else {
|
|
13473
|
+
console.log(` ${role} (PID ${entry.pid}) already exited`);
|
|
13474
|
+
}
|
|
13475
|
+
return result;
|
|
13476
|
+
}));
|
|
13477
|
+
await ctx.deps.clock.delay(AGENT_SHUTDOWN_TIMEOUT_MS);
|
|
13478
|
+
for (const { role, entry } of agents) {
|
|
13479
|
+
try {
|
|
13480
|
+
ctx.deps.processes.kill(entry.pid, 0);
|
|
13481
|
+
ctx.deps.processes.kill(entry.pid, "SIGKILL");
|
|
13482
|
+
console.log(` Force-killed ${role} (PID ${entry.pid})`);
|
|
13483
|
+
} catch {}
|
|
13484
|
+
}
|
|
13485
|
+
console.log(`[${formatTimestamp()}] All agents stopped`);
|
|
13486
|
+
}
|
|
13487
|
+
try {
|
|
13488
|
+
await ctx.deps.backend.mutation(api.machines.updateDaemonStatus, {
|
|
13489
|
+
sessionId: ctx.sessionId,
|
|
13490
|
+
machineId: ctx.machineId,
|
|
13491
|
+
connected: false
|
|
13492
|
+
});
|
|
13493
|
+
} catch {}
|
|
13494
|
+
}
|
|
13495
|
+
var AGENT_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
13496
|
+
var init_on_daemon_shutdown = __esm(() => {
|
|
13497
|
+
init_api3();
|
|
13498
|
+
init_on_agent_shutdown();
|
|
13499
|
+
});
|
|
13500
|
+
|
|
12886
13501
|
// src/commands/machine/pid.ts
|
|
12887
13502
|
import { createHash } from "node:crypto";
|
|
12888
|
-
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as
|
|
13503
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
|
|
12889
13504
|
import { homedir as homedir4 } from "node:os";
|
|
12890
|
-
import { join as
|
|
13505
|
+
import { join as join5 } from "node:path";
|
|
12891
13506
|
function getUrlHash() {
|
|
12892
13507
|
const url = getConvexUrl();
|
|
12893
13508
|
return createHash("sha256").update(url).digest("hex").substring(0, 8);
|
|
@@ -12901,7 +13516,7 @@ function ensureChatroomDir() {
|
|
|
12901
13516
|
}
|
|
12902
13517
|
}
|
|
12903
13518
|
function getPidFilePath() {
|
|
12904
|
-
return
|
|
13519
|
+
return join5(CHATROOM_DIR4, getPidFileName());
|
|
12905
13520
|
}
|
|
12906
13521
|
function isProcessRunning(pid) {
|
|
12907
13522
|
try {
|
|
@@ -12930,13 +13545,13 @@ function readPid() {
|
|
|
12930
13545
|
function writePid() {
|
|
12931
13546
|
ensureChatroomDir();
|
|
12932
13547
|
const pidPath = getPidFilePath();
|
|
12933
|
-
|
|
13548
|
+
writeFileSync4(pidPath, process.pid.toString(), "utf-8");
|
|
12934
13549
|
}
|
|
12935
13550
|
function removePid() {
|
|
12936
13551
|
const pidPath = getPidFilePath();
|
|
12937
13552
|
try {
|
|
12938
13553
|
if (existsSync4(pidPath)) {
|
|
12939
|
-
|
|
13554
|
+
unlinkSync2(pidPath);
|
|
12940
13555
|
}
|
|
12941
13556
|
} catch {}
|
|
12942
13557
|
}
|
|
@@ -12966,171 +13581,16 @@ function releaseLock() {
|
|
|
12966
13581
|
var CHATROOM_DIR4;
|
|
12967
13582
|
var init_pid = __esm(() => {
|
|
12968
13583
|
init_client2();
|
|
12969
|
-
CHATROOM_DIR4 =
|
|
13584
|
+
CHATROOM_DIR4 = join5(homedir4(), ".chatroom");
|
|
12970
13585
|
});
|
|
12971
13586
|
|
|
12972
|
-
// src/commands/machine/daemon-start.ts
|
|
12973
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
12974
|
-
import { stat } from "node:fs/promises";
|
|
12975
|
-
function parseMachineCommand(raw) {
|
|
12976
|
-
switch (raw.type) {
|
|
12977
|
-
case "ping":
|
|
12978
|
-
return { _id: raw._id, type: "ping", payload: {}, createdAt: raw.createdAt };
|
|
12979
|
-
case "status":
|
|
12980
|
-
return { _id: raw._id, type: "status", payload: {}, createdAt: raw.createdAt };
|
|
12981
|
-
case "start-agent": {
|
|
12982
|
-
const { chatroomId, role, agentHarness } = raw.payload;
|
|
12983
|
-
if (!chatroomId || !role || !agentHarness) {
|
|
12984
|
-
console.error(` ⚠️ Invalid start-agent command: missing chatroomId, role, or agentHarness`);
|
|
12985
|
-
return null;
|
|
12986
|
-
}
|
|
12987
|
-
return {
|
|
12988
|
-
_id: raw._id,
|
|
12989
|
-
type: "start-agent",
|
|
12990
|
-
payload: {
|
|
12991
|
-
chatroomId,
|
|
12992
|
-
role,
|
|
12993
|
-
agentHarness,
|
|
12994
|
-
model: raw.payload.model,
|
|
12995
|
-
workingDir: raw.payload.workingDir
|
|
12996
|
-
},
|
|
12997
|
-
createdAt: raw.createdAt
|
|
12998
|
-
};
|
|
12999
|
-
}
|
|
13000
|
-
case "stop-agent": {
|
|
13001
|
-
const { chatroomId, role } = raw.payload;
|
|
13002
|
-
if (!chatroomId || !role) {
|
|
13003
|
-
console.error(` ⚠️ Invalid stop-agent command: missing chatroomId or role`);
|
|
13004
|
-
return null;
|
|
13005
|
-
}
|
|
13006
|
-
return {
|
|
13007
|
-
_id: raw._id,
|
|
13008
|
-
type: "stop-agent",
|
|
13009
|
-
payload: { chatroomId, role },
|
|
13010
|
-
createdAt: raw.createdAt
|
|
13011
|
-
};
|
|
13012
|
-
}
|
|
13013
|
-
default:
|
|
13014
|
-
return null;
|
|
13015
|
-
}
|
|
13016
|
-
}
|
|
13017
|
-
function formatTimestamp() {
|
|
13018
|
-
return new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
13019
|
-
}
|
|
13020
|
-
function verifyPidOwnership(pid, expectedHarness) {
|
|
13021
|
-
try {
|
|
13022
|
-
process.kill(pid, 0);
|
|
13023
|
-
} catch {
|
|
13024
|
-
return false;
|
|
13025
|
-
}
|
|
13026
|
-
if (!expectedHarness) {
|
|
13027
|
-
return true;
|
|
13028
|
-
}
|
|
13029
|
-
try {
|
|
13030
|
-
const platform = process.platform;
|
|
13031
|
-
let processName = "";
|
|
13032
|
-
if (platform === "darwin" || platform === "linux") {
|
|
13033
|
-
processName = execSync3(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
13034
|
-
encoding: "utf-8",
|
|
13035
|
-
timeout: 3000
|
|
13036
|
-
}).trim();
|
|
13037
|
-
}
|
|
13038
|
-
if (!processName) {
|
|
13039
|
-
return true;
|
|
13040
|
-
}
|
|
13041
|
-
const harnessLower = expectedHarness.toLowerCase();
|
|
13042
|
-
const procLower = processName.toLowerCase();
|
|
13043
|
-
return procLower.includes(harnessLower) || procLower.includes("node") || procLower.includes("bun");
|
|
13044
|
-
} catch {
|
|
13045
|
-
return true;
|
|
13046
|
-
}
|
|
13047
|
-
}
|
|
13048
|
-
async function clearAgentPidEverywhere(ctx, chatroomId, role) {
|
|
13049
|
-
try {
|
|
13050
|
-
await ctx.client.mutation(api.machines.updateSpawnedAgent, {
|
|
13051
|
-
sessionId: ctx.sessionId,
|
|
13052
|
-
machineId: ctx.machineId,
|
|
13053
|
-
chatroomId,
|
|
13054
|
-
role,
|
|
13055
|
-
pid: undefined
|
|
13056
|
-
});
|
|
13057
|
-
} catch (e) {
|
|
13058
|
-
console.log(` ⚠️ Failed to clear PID in backend: ${e.message}`);
|
|
13059
|
-
}
|
|
13060
|
-
clearAgentPid(ctx.machineId, chatroomId, role);
|
|
13061
|
-
}
|
|
13062
|
-
async function handleAgentCrashRecovery(ctx, originalCommand, _crashedPid) {
|
|
13063
|
-
const { chatroomId, role } = originalCommand.payload;
|
|
13064
|
-
const ts = formatTimestamp();
|
|
13065
|
-
await clearAgentPidEverywhere(ctx, chatroomId, role).catch((err) => {
|
|
13066
|
-
console.log(` ⚠️ Failed to clear PID after exit: ${err.message}`);
|
|
13067
|
-
});
|
|
13068
|
-
try {
|
|
13069
|
-
await ctx.client.mutation(api.participants.leave, {
|
|
13070
|
-
sessionId: ctx.sessionId,
|
|
13071
|
-
chatroomId,
|
|
13072
|
-
role
|
|
13073
|
-
});
|
|
13074
|
-
console.log(`[${ts}] Marked ${role} as offline (participant removed)`);
|
|
13075
|
-
} catch (leaveErr) {
|
|
13076
|
-
console.log(`[${ts}] ⚠️ Could not remove participant: ${leaveErr.message}`);
|
|
13077
|
-
}
|
|
13078
|
-
console.log(`[${ts}] \uD83D\uDD04 Attempting to restart ${role} (max ${MAX_CRASH_RESTART_ATTEMPTS} attempts)...`);
|
|
13079
|
-
for (let attempt = 1;attempt <= MAX_CRASH_RESTART_ATTEMPTS; attempt++) {
|
|
13080
|
-
const attemptTs = formatTimestamp();
|
|
13081
|
-
console.log(`[${attemptTs}] Restart attempt ${attempt}/${MAX_CRASH_RESTART_ATTEMPTS}...`);
|
|
13082
|
-
await new Promise((resolve2) => setTimeout(resolve2, CRASH_RESTART_DELAY_MS));
|
|
13083
|
-
try {
|
|
13084
|
-
const result = await handleStartAgent(ctx, originalCommand);
|
|
13085
|
-
if (!result.failed) {
|
|
13086
|
-
const successTs = formatTimestamp();
|
|
13087
|
-
console.log(`[${successTs}] ✅ ${role} restarted successfully on attempt ${attempt}`);
|
|
13088
|
-
return;
|
|
13089
|
-
}
|
|
13090
|
-
console.log(`[${attemptTs}] ⚠️ Restart attempt ${attempt} failed: ${result.result}`);
|
|
13091
|
-
} catch (restartErr) {
|
|
13092
|
-
console.log(`[${attemptTs}] ⚠️ Restart attempt ${attempt} error: ${restartErr.message}`);
|
|
13093
|
-
}
|
|
13094
|
-
}
|
|
13095
|
-
const failTs = formatTimestamp();
|
|
13096
|
-
console.log(`[${failTs}] ❌ Failed to restart ${role} after ${MAX_CRASH_RESTART_ATTEMPTS} attempts. ` + `The agent will need to be restarted manually or via the webapp.`);
|
|
13097
|
-
}
|
|
13098
|
-
async function recoverAgentState(ctx) {
|
|
13099
|
-
const entries = listAgentEntries(ctx.machineId);
|
|
13100
|
-
if (entries.length === 0) {
|
|
13101
|
-
console.log(` No agent entries found — nothing to recover`);
|
|
13102
|
-
return;
|
|
13103
|
-
}
|
|
13104
|
-
let recovered = 0;
|
|
13105
|
-
let cleared = 0;
|
|
13106
|
-
for (const { chatroomId, role, entry } of entries) {
|
|
13107
|
-
const { pid, harness } = entry;
|
|
13108
|
-
const alive = verifyPidOwnership(pid, harness);
|
|
13109
|
-
if (alive) {
|
|
13110
|
-
console.log(` ✅ Recovered: ${role} (PID ${pid}, harness: ${harness})`);
|
|
13111
|
-
recovered++;
|
|
13112
|
-
} else {
|
|
13113
|
-
console.log(` \uD83E\uDDF9 Stale PID ${pid} for ${role} — clearing`);
|
|
13114
|
-
await clearAgentPidEverywhere(ctx, chatroomId, role);
|
|
13115
|
-
cleared++;
|
|
13116
|
-
}
|
|
13117
|
-
}
|
|
13118
|
-
console.log(` Recovery complete: ${recovered} alive, ${cleared} stale cleared`);
|
|
13119
|
-
}
|
|
13587
|
+
// src/commands/machine/daemon-start/handlers/ping.ts
|
|
13120
13588
|
function handlePing() {
|
|
13121
13589
|
console.log(` ↪ Responding: pong`);
|
|
13122
13590
|
return { result: "pong", failed: false };
|
|
13123
13591
|
}
|
|
13124
|
-
|
|
13125
|
-
|
|
13126
|
-
hostname: ctx.config?.hostname,
|
|
13127
|
-
os: ctx.config?.os,
|
|
13128
|
-
availableHarnesses: ctx.config?.availableHarnesses,
|
|
13129
|
-
chatroomAgents: Object.keys(ctx.config?.chatroomAgents ?? {})
|
|
13130
|
-
});
|
|
13131
|
-
console.log(` ↪ Responding with status`);
|
|
13132
|
-
return { result, failed: false };
|
|
13133
|
-
}
|
|
13592
|
+
|
|
13593
|
+
// src/commands/machine/daemon-start/handlers/start-agent.ts
|
|
13134
13594
|
async function handleStartAgent(ctx, command) {
|
|
13135
13595
|
const { chatroomId, role, agentHarness, model, workingDir } = command.payload;
|
|
13136
13596
|
console.log(` ↪ start-agent command received`);
|
|
@@ -13140,100 +13600,163 @@ async function handleStartAgent(ctx, command) {
|
|
|
13140
13600
|
if (model) {
|
|
13141
13601
|
console.log(` Model: ${model}`);
|
|
13142
13602
|
}
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
console.log(`
|
|
13146
|
-
|
|
13147
|
-
agentContext = getAgentContext(chatroomId, role);
|
|
13148
|
-
}
|
|
13149
|
-
if (!agentContext) {
|
|
13150
|
-
const msg = `No agent context found for ${chatroomId}/${role}`;
|
|
13151
|
-
console.log(` ⚠️ ${msg}`);
|
|
13152
|
-
return { result: msg, failed: true };
|
|
13603
|
+
if (!workingDir) {
|
|
13604
|
+
const msg2 = `No workingDir provided in command payload for ${chatroomId}/${role}`;
|
|
13605
|
+
console.log(` ⚠️ ${msg2}`);
|
|
13606
|
+
return { result: msg2, failed: true };
|
|
13153
13607
|
}
|
|
13154
|
-
console.log(` Working dir: ${
|
|
13608
|
+
console.log(` Working dir: ${workingDir}`);
|
|
13155
13609
|
try {
|
|
13156
|
-
const dirStat = await stat(
|
|
13610
|
+
const dirStat = await ctx.deps.fs.stat(workingDir);
|
|
13157
13611
|
if (!dirStat.isDirectory()) {
|
|
13158
|
-
const
|
|
13159
|
-
console.log(` ⚠️ ${
|
|
13160
|
-
return { result:
|
|
13612
|
+
const msg2 = `Working directory is not a directory: ${workingDir}`;
|
|
13613
|
+
console.log(` ⚠️ ${msg2}`);
|
|
13614
|
+
return { result: msg2, failed: true };
|
|
13161
13615
|
}
|
|
13162
13616
|
} catch {
|
|
13163
|
-
const
|
|
13164
|
-
console.log(` ⚠️ ${
|
|
13165
|
-
return { result:
|
|
13617
|
+
const msg2 = `Working directory does not exist: ${workingDir}`;
|
|
13618
|
+
console.log(` ⚠️ ${msg2}`);
|
|
13619
|
+
return { result: msg2, failed: true };
|
|
13620
|
+
}
|
|
13621
|
+
try {
|
|
13622
|
+
const existingConfigs = await ctx.deps.backend.query(api.machines.getAgentConfigs, {
|
|
13623
|
+
sessionId: ctx.sessionId,
|
|
13624
|
+
chatroomId
|
|
13625
|
+
});
|
|
13626
|
+
const existingConfig = existingConfigs.configs.find((c) => c.machineId === ctx.machineId && c.role.toLowerCase() === role.toLowerCase());
|
|
13627
|
+
if (existingConfig?.spawnedAgentPid) {
|
|
13628
|
+
const existingPid = existingConfig.spawnedAgentPid;
|
|
13629
|
+
const isAlive = ctx.remoteAgentService.isAlive(existingPid);
|
|
13630
|
+
if (isAlive) {
|
|
13631
|
+
console.log(` ⚠️ Existing agent detected (PID: ${existingPid}) — stopping before respawn`);
|
|
13632
|
+
await onAgentShutdown(ctx, { chatroomId, role, pid: existingPid });
|
|
13633
|
+
console.log(` ✅ Existing agent stopped`);
|
|
13634
|
+
}
|
|
13635
|
+
}
|
|
13636
|
+
} catch (e) {
|
|
13637
|
+
console.log(` ⚠️ Could not check for existing agent (proceeding): ${e.message}`);
|
|
13166
13638
|
}
|
|
13167
13639
|
const convexUrl = getConvexUrl();
|
|
13168
|
-
const initPromptResult = await ctx.
|
|
13640
|
+
const initPromptResult = await ctx.deps.backend.query(api.messages.getInitPrompt, {
|
|
13169
13641
|
sessionId: ctx.sessionId,
|
|
13170
13642
|
chatroomId,
|
|
13171
13643
|
role,
|
|
13172
13644
|
convexUrl
|
|
13173
13645
|
});
|
|
13174
13646
|
if (!initPromptResult?.prompt) {
|
|
13175
|
-
const
|
|
13176
|
-
console.log(` ⚠️ ${
|
|
13177
|
-
return { result:
|
|
13647
|
+
const msg2 = "Failed to fetch init prompt from backend";
|
|
13648
|
+
console.log(` ⚠️ ${msg2}`);
|
|
13649
|
+
return { result: msg2, failed: true };
|
|
13178
13650
|
}
|
|
13179
13651
|
console.log(` Fetched split init prompt from backend`);
|
|
13180
|
-
const
|
|
13181
|
-
|
|
13182
|
-
|
|
13652
|
+
const combinedPrompt = `${initPromptResult.rolePrompt}
|
|
13653
|
+
|
|
13654
|
+
${initPromptResult.initialMessage}`;
|
|
13655
|
+
let spawnResult;
|
|
13183
13656
|
try {
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13657
|
+
spawnResult = await ctx.remoteAgentService.spawn({
|
|
13658
|
+
workingDir,
|
|
13659
|
+
prompt: combinedPrompt,
|
|
13660
|
+
model,
|
|
13661
|
+
context: { machineId: ctx.machineId, chatroomId, role }
|
|
13662
|
+
});
|
|
13663
|
+
} catch (e) {
|
|
13664
|
+
const msg2 = `Failed to spawn agent: ${e.message}`;
|
|
13665
|
+
console.log(` ⚠️ ${msg2}`);
|
|
13666
|
+
return { result: msg2, failed: true };
|
|
13667
|
+
}
|
|
13668
|
+
const { pid } = spawnResult;
|
|
13669
|
+
const msg = `Agent spawned (PID: ${pid})`;
|
|
13670
|
+
console.log(` ✅ ${msg}`);
|
|
13671
|
+
try {
|
|
13672
|
+
await ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
13673
|
+
sessionId: ctx.sessionId,
|
|
13674
|
+
machineId: ctx.machineId,
|
|
13675
|
+
chatroomId,
|
|
13676
|
+
role,
|
|
13677
|
+
pid,
|
|
13678
|
+
model
|
|
13679
|
+
});
|
|
13680
|
+
console.log(` Updated backend with PID: ${pid}`);
|
|
13681
|
+
ctx.deps.machine.persistAgentPid(ctx.machineId, chatroomId, role, pid, agentHarness);
|
|
13682
|
+
} catch (e) {
|
|
13683
|
+
console.log(` ⚠️ Failed to update PID in backend: ${e.message}`);
|
|
13189
13684
|
}
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13685
|
+
ctx.events.emit("agent:started", {
|
|
13686
|
+
chatroomId,
|
|
13687
|
+
role,
|
|
13688
|
+
pid,
|
|
13689
|
+
harness: agentHarness,
|
|
13195
13690
|
model
|
|
13196
13691
|
});
|
|
13197
|
-
|
|
13198
|
-
const
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
const ts = formatTimestamp();
|
|
13219
|
-
console.log(`[${ts}] ⚠️ Agent process exited unexpectedly ` + `(PID: ${spawnedPid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
13220
|
-
handleAgentCrashRecovery(ctx, command, spawnedPid).catch((err) => {
|
|
13221
|
-
console.log(` ⚠️ Crash recovery failed for ${role}: ${err.message}`);
|
|
13222
|
-
});
|
|
13223
|
-
});
|
|
13224
|
-
}
|
|
13692
|
+
spawnResult.onExit(({ code: code2, signal }) => {
|
|
13693
|
+
const wasIntentional = ctx.deps.stops.consume(chatroomId, role);
|
|
13694
|
+
ctx.events.emit("agent:exited", {
|
|
13695
|
+
chatroomId,
|
|
13696
|
+
role,
|
|
13697
|
+
pid,
|
|
13698
|
+
code: code2,
|
|
13699
|
+
signal,
|
|
13700
|
+
intentional: wasIntentional
|
|
13701
|
+
});
|
|
13702
|
+
});
|
|
13703
|
+
let lastReportedTokenAt = 0;
|
|
13704
|
+
spawnResult.onOutput(() => {
|
|
13705
|
+
const now = Date.now();
|
|
13706
|
+
if (now - lastReportedTokenAt >= 30000) {
|
|
13707
|
+
lastReportedTokenAt = now;
|
|
13708
|
+
ctx.deps.backend.mutation(api.participants.updateTokenActivity, {
|
|
13709
|
+
sessionId: ctx.sessionId,
|
|
13710
|
+
chatroomId,
|
|
13711
|
+
role
|
|
13712
|
+
}).catch(() => {});
|
|
13225
13713
|
}
|
|
13226
|
-
|
|
13714
|
+
});
|
|
13715
|
+
return { result: msg, failed: false };
|
|
13716
|
+
}
|
|
13717
|
+
var init_start_agent = __esm(() => {
|
|
13718
|
+
init_api3();
|
|
13719
|
+
init_client2();
|
|
13720
|
+
init_on_agent_shutdown();
|
|
13721
|
+
});
|
|
13722
|
+
|
|
13723
|
+
// src/commands/machine/daemon-start/handlers/status.ts
|
|
13724
|
+
function handleStatus(ctx) {
|
|
13725
|
+
const result = JSON.stringify({
|
|
13726
|
+
hostname: ctx.config?.hostname,
|
|
13727
|
+
os: ctx.config?.os,
|
|
13728
|
+
availableHarnesses: ctx.config?.availableHarnesses
|
|
13729
|
+
});
|
|
13730
|
+
console.log(` ↪ Responding with status`);
|
|
13731
|
+
return { result, failed: false };
|
|
13732
|
+
}
|
|
13733
|
+
|
|
13734
|
+
// src/commands/machine/daemon-start/handlers/shared.ts
|
|
13735
|
+
async function clearAgentPidEverywhere(ctx, chatroomId, role) {
|
|
13736
|
+
try {
|
|
13737
|
+
await ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
13738
|
+
sessionId: ctx.sessionId,
|
|
13739
|
+
machineId: ctx.machineId,
|
|
13740
|
+
chatroomId,
|
|
13741
|
+
role,
|
|
13742
|
+
pid: undefined
|
|
13743
|
+
});
|
|
13744
|
+
} catch (e) {
|
|
13745
|
+
console.log(` ⚠️ Failed to clear PID in backend: ${e.message}`);
|
|
13227
13746
|
}
|
|
13228
|
-
|
|
13229
|
-
return { result: startResult.message, failed: true };
|
|
13747
|
+
ctx.deps.machine.clearAgentPid(ctx.machineId, chatroomId, role);
|
|
13230
13748
|
}
|
|
13749
|
+
var init_shared = __esm(() => {
|
|
13750
|
+
init_api3();
|
|
13751
|
+
});
|
|
13752
|
+
|
|
13753
|
+
// src/commands/machine/daemon-start/handlers/stop-agent.ts
|
|
13231
13754
|
async function handleStopAgent(ctx, command) {
|
|
13232
13755
|
const { chatroomId, role } = command.payload;
|
|
13233
13756
|
console.log(` ↪ stop-agent command received`);
|
|
13234
13757
|
console.log(` Chatroom: ${chatroomId}`);
|
|
13235
13758
|
console.log(` Role: ${role}`);
|
|
13236
|
-
const configsResult = await ctx.
|
|
13759
|
+
const configsResult = await ctx.deps.backend.query(api.machines.getAgentConfigs, {
|
|
13237
13760
|
sessionId: ctx.sessionId,
|
|
13238
13761
|
chatroomId
|
|
13239
13762
|
});
|
|
@@ -13244,28 +13767,14 @@ async function handleStopAgent(ctx, command) {
|
|
|
13244
13767
|
return { result: msg, failed: true };
|
|
13245
13768
|
}
|
|
13246
13769
|
const pidToKill = targetConfig.spawnedAgentPid;
|
|
13247
|
-
const agentHarness = targetConfig.agentType || undefined;
|
|
13248
13770
|
console.log(` Stopping agent with PID: ${pidToKill}`);
|
|
13249
|
-
const
|
|
13250
|
-
harness: agentHarness || "opencode",
|
|
13251
|
-
type: "process",
|
|
13252
|
-
pid: pidToKill,
|
|
13253
|
-
workingDir: ""
|
|
13254
|
-
};
|
|
13255
|
-
const registry = getDriverRegistry();
|
|
13256
|
-
let stopDriver;
|
|
13257
|
-
try {
|
|
13258
|
-
stopDriver = agentHarness ? registry.get(agentHarness) : null;
|
|
13259
|
-
} catch {
|
|
13260
|
-
stopDriver = null;
|
|
13261
|
-
}
|
|
13262
|
-
const isAlive = stopDriver ? await stopDriver.isAlive(stopHandle) : verifyPidOwnership(pidToKill, agentHarness);
|
|
13771
|
+
const isAlive = ctx.remoteAgentService.isAlive(pidToKill);
|
|
13263
13772
|
if (!isAlive) {
|
|
13264
13773
|
console.log(` ⚠️ PID ${pidToKill} does not appear to belong to the expected agent`);
|
|
13265
13774
|
await clearAgentPidEverywhere(ctx, chatroomId, role);
|
|
13266
13775
|
console.log(` Cleared stale PID`);
|
|
13267
13776
|
try {
|
|
13268
|
-
await ctx.
|
|
13777
|
+
await ctx.deps.backend.mutation(api.participants.leave, {
|
|
13269
13778
|
sessionId: ctx.sessionId,
|
|
13270
13779
|
chatroomId,
|
|
13271
13780
|
role
|
|
@@ -13278,54 +13787,58 @@ async function handleStopAgent(ctx, command) {
|
|
|
13278
13787
|
};
|
|
13279
13788
|
}
|
|
13280
13789
|
try {
|
|
13281
|
-
|
|
13282
|
-
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
}
|
|
13286
|
-
const msg = `Agent stopped (PID: ${pidToKill})`;
|
|
13287
|
-
console.log(` ✅ ${msg}`);
|
|
13288
|
-
|
|
13289
|
-
console.log(` Cleared PID`);
|
|
13290
|
-
try {
|
|
13291
|
-
await ctx.client.mutation(api.participants.leave, {
|
|
13292
|
-
sessionId: ctx.sessionId,
|
|
13293
|
-
chatroomId,
|
|
13294
|
-
role
|
|
13295
|
-
});
|
|
13296
|
-
console.log(` Removed participant record`);
|
|
13297
|
-
} catch (leaveErr) {
|
|
13298
|
-
console.log(` ⚠️ Could not remove participant: ${leaveErr.message}`);
|
|
13299
|
-
}
|
|
13300
|
-
return { result: msg, failed: false };
|
|
13790
|
+
const shutdownResult = await onAgentShutdown(ctx, {
|
|
13791
|
+
chatroomId,
|
|
13792
|
+
role,
|
|
13793
|
+
pid: pidToKill
|
|
13794
|
+
});
|
|
13795
|
+
const msg = shutdownResult.killed ? `Agent stopped (PID: ${pidToKill})` : `Agent stop attempted (PID: ${pidToKill}) — process may still be running`;
|
|
13796
|
+
console.log(` ${shutdownResult.killed ? "✅" : "⚠️ "} ${msg}`);
|
|
13797
|
+
return { result: msg, failed: !shutdownResult.killed };
|
|
13301
13798
|
} catch (e) {
|
|
13302
|
-
const
|
|
13303
|
-
if (err.code === "ESRCH") {
|
|
13304
|
-
await clearAgentPidEverywhere(ctx, chatroomId, role);
|
|
13305
|
-
try {
|
|
13306
|
-
await ctx.client.mutation(api.participants.leave, {
|
|
13307
|
-
sessionId: ctx.sessionId,
|
|
13308
|
-
chatroomId,
|
|
13309
|
-
role
|
|
13310
|
-
});
|
|
13311
|
-
} catch {}
|
|
13312
|
-
const msg2 = "Process not found (may have already exited)";
|
|
13313
|
-
console.log(` ⚠️ ${msg2}`);
|
|
13314
|
-
return { result: msg2, failed: true };
|
|
13315
|
-
}
|
|
13316
|
-
const msg = `Failed to stop agent: ${err.message}`;
|
|
13799
|
+
const msg = `Failed to stop agent: ${e.message}`;
|
|
13317
13800
|
console.log(` ⚠️ ${msg}`);
|
|
13318
13801
|
return { result: msg, failed: true };
|
|
13319
13802
|
}
|
|
13320
13803
|
}
|
|
13804
|
+
var init_stop_agent = __esm(() => {
|
|
13805
|
+
init_api3();
|
|
13806
|
+
init_on_agent_shutdown();
|
|
13807
|
+
init_shared();
|
|
13808
|
+
});
|
|
13809
|
+
|
|
13810
|
+
// src/commands/machine/daemon-start/command-loop.ts
|
|
13811
|
+
async function refreshModels(ctx) {
|
|
13812
|
+
const models = await ctx.remoteAgentService.listModels();
|
|
13813
|
+
if (!ctx.config)
|
|
13814
|
+
return;
|
|
13815
|
+
try {
|
|
13816
|
+
await ctx.deps.backend.mutation(api.machines.register, {
|
|
13817
|
+
sessionId: ctx.sessionId,
|
|
13818
|
+
machineId: ctx.machineId,
|
|
13819
|
+
hostname: ctx.config.hostname,
|
|
13820
|
+
os: ctx.config.os,
|
|
13821
|
+
availableHarnesses: ctx.config.availableHarnesses,
|
|
13822
|
+
harnessVersions: ctx.config.harnessVersions,
|
|
13823
|
+
availableModels: models
|
|
13824
|
+
});
|
|
13825
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh: ${models.length > 0 ? `${models.length} models` : "none discovered"}`);
|
|
13826
|
+
} catch (error) {
|
|
13827
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${error.message}`);
|
|
13828
|
+
}
|
|
13829
|
+
}
|
|
13321
13830
|
async function processCommand(ctx, command) {
|
|
13322
13831
|
console.log(`[${formatTimestamp()}] \uD83D\uDCE8 Command received: ${command.type}`);
|
|
13323
13832
|
try {
|
|
13324
|
-
await ctx.
|
|
13833
|
+
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
13325
13834
|
sessionId: ctx.sessionId,
|
|
13326
13835
|
commandId: command._id,
|
|
13327
13836
|
status: "processing"
|
|
13328
13837
|
});
|
|
13838
|
+
ctx.events.emit("command:processing", {
|
|
13839
|
+
commandId: command._id.toString(),
|
|
13840
|
+
type: command.type
|
|
13841
|
+
});
|
|
13329
13842
|
let commandResult;
|
|
13330
13843
|
switch (command.type) {
|
|
13331
13844
|
case "ping":
|
|
@@ -13349,147 +13862,39 @@ async function processCommand(ctx, command) {
|
|
|
13349
13862
|
}
|
|
13350
13863
|
}
|
|
13351
13864
|
const finalStatus = commandResult.failed ? "failed" : "completed";
|
|
13352
|
-
await ctx.
|
|
13865
|
+
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
13353
13866
|
sessionId: ctx.sessionId,
|
|
13354
13867
|
commandId: command._id,
|
|
13355
13868
|
status: finalStatus,
|
|
13356
13869
|
result: commandResult.result
|
|
13357
13870
|
});
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
} catch (error) {
|
|
13364
|
-
console.error(` ❌ Command failed: ${error.message}`);
|
|
13365
|
-
try {
|
|
13366
|
-
await ctx.client.mutation(api.machines.ackCommand, {
|
|
13367
|
-
sessionId: ctx.sessionId,
|
|
13368
|
-
commandId: command._id,
|
|
13369
|
-
status: "failed",
|
|
13370
|
-
result: error.message
|
|
13371
|
-
});
|
|
13372
|
-
} catch {}
|
|
13373
|
-
}
|
|
13374
|
-
}
|
|
13375
|
-
async function discoverModels() {
|
|
13376
|
-
const models = [];
|
|
13377
|
-
try {
|
|
13378
|
-
const registry = getDriverRegistry();
|
|
13379
|
-
for (const driver of registry.all()) {
|
|
13380
|
-
if (driver.capabilities.dynamicModelDiscovery) {
|
|
13381
|
-
const driverModels = await driver.listModels();
|
|
13382
|
-
models.push(...driverModels);
|
|
13383
|
-
}
|
|
13384
|
-
}
|
|
13385
|
-
} catch {}
|
|
13386
|
-
return models;
|
|
13387
|
-
}
|
|
13388
|
-
async function refreshModels(ctx) {
|
|
13389
|
-
const models = await discoverModels();
|
|
13390
|
-
if (!ctx.config)
|
|
13391
|
-
return;
|
|
13392
|
-
try {
|
|
13393
|
-
await ctx.client.mutation(api.machines.register, {
|
|
13394
|
-
sessionId: ctx.sessionId,
|
|
13395
|
-
machineId: ctx.machineId,
|
|
13396
|
-
hostname: ctx.config.hostname,
|
|
13397
|
-
os: ctx.config.os,
|
|
13398
|
-
availableHarnesses: ctx.config.availableHarnesses,
|
|
13399
|
-
harnessVersions: ctx.config.harnessVersions,
|
|
13400
|
-
availableModels: models
|
|
13401
|
-
});
|
|
13402
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh: ${models.length > 0 ? `${models.length} models` : "none discovered"}`);
|
|
13403
|
-
} catch (error) {
|
|
13404
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${error.message}`);
|
|
13405
|
-
}
|
|
13406
|
-
}
|
|
13407
|
-
async function initDaemon() {
|
|
13408
|
-
if (!acquireLock()) {
|
|
13409
|
-
process.exit(1);
|
|
13410
|
-
}
|
|
13411
|
-
const convexUrl = getConvexUrl();
|
|
13412
|
-
const sessionId = getSessionId();
|
|
13413
|
-
if (!sessionId) {
|
|
13414
|
-
const otherUrls = getOtherSessionUrls();
|
|
13415
|
-
console.error(`❌ Not authenticated for: ${convexUrl}`);
|
|
13416
|
-
if (otherUrls.length > 0) {
|
|
13417
|
-
console.error(`
|
|
13418
|
-
\uD83D\uDCA1 You have sessions for other environments:`);
|
|
13419
|
-
for (const url of otherUrls) {
|
|
13420
|
-
console.error(` • ${url}`);
|
|
13421
|
-
}
|
|
13422
|
-
}
|
|
13423
|
-
console.error(`
|
|
13424
|
-
Run: chatroom auth login`);
|
|
13425
|
-
releaseLock();
|
|
13426
|
-
process.exit(1);
|
|
13427
|
-
}
|
|
13428
|
-
const machineId = getMachineId();
|
|
13429
|
-
if (!machineId) {
|
|
13430
|
-
console.error(`❌ Machine not registered`);
|
|
13431
|
-
console.error(`
|
|
13432
|
-
Run any chatroom command first to register this machine,`);
|
|
13433
|
-
console.error(`for example: chatroom auth status`);
|
|
13434
|
-
releaseLock();
|
|
13435
|
-
process.exit(1);
|
|
13436
|
-
}
|
|
13437
|
-
const client2 = await getConvexClient();
|
|
13438
|
-
const typedSessionId = sessionId;
|
|
13439
|
-
const config3 = loadMachineConfig();
|
|
13440
|
-
const availableModels = await discoverModels();
|
|
13441
|
-
if (config3) {
|
|
13442
|
-
try {
|
|
13443
|
-
await client2.mutation(api.machines.register, {
|
|
13444
|
-
sessionId: typedSessionId,
|
|
13445
|
-
machineId,
|
|
13446
|
-
hostname: config3.hostname,
|
|
13447
|
-
os: config3.os,
|
|
13448
|
-
availableHarnesses: config3.availableHarnesses,
|
|
13449
|
-
harnessVersions: config3.harnessVersions,
|
|
13450
|
-
availableModels
|
|
13451
|
-
});
|
|
13452
|
-
} catch (error) {
|
|
13453
|
-
console.warn(`⚠️ Machine registration update failed: ${error.message}`);
|
|
13454
|
-
}
|
|
13455
|
-
}
|
|
13456
|
-
try {
|
|
13457
|
-
await client2.mutation(api.machines.updateDaemonStatus, {
|
|
13458
|
-
sessionId: typedSessionId,
|
|
13459
|
-
machineId,
|
|
13460
|
-
connected: true
|
|
13871
|
+
ctx.events.emit("command:completed", {
|
|
13872
|
+
commandId: command._id.toString(),
|
|
13873
|
+
type: command.type,
|
|
13874
|
+
failed: commandResult.failed,
|
|
13875
|
+
result: commandResult.result
|
|
13461
13876
|
});
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
formatConnectivityError(error, convexUrl);
|
|
13877
|
+
if (commandResult.failed) {
|
|
13878
|
+
console.log(` ❌ Command failed: ${commandResult.result}`);
|
|
13465
13879
|
} else {
|
|
13466
|
-
console.
|
|
13880
|
+
console.log(` ✅ Command completed`);
|
|
13467
13881
|
}
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
console.log(` PID: ${process.pid}`);
|
|
13479
|
-
console.log(`
|
|
13480
|
-
[${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
|
|
13481
|
-
try {
|
|
13482
|
-
await recoverAgentState(ctx);
|
|
13483
|
-
} catch (e) {
|
|
13484
|
-
console.log(` ⚠️ Recovery failed: ${e.message}`);
|
|
13485
|
-
console.log(` Continuing with fresh state`);
|
|
13882
|
+
} catch (error) {
|
|
13883
|
+
console.error(` ❌ Command failed: ${error.message}`);
|
|
13884
|
+
try {
|
|
13885
|
+
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
13886
|
+
sessionId: ctx.sessionId,
|
|
13887
|
+
commandId: command._id,
|
|
13888
|
+
status: "failed",
|
|
13889
|
+
result: error.message
|
|
13890
|
+
});
|
|
13891
|
+
} catch {}
|
|
13486
13892
|
}
|
|
13487
|
-
return ctx;
|
|
13488
13893
|
}
|
|
13489
13894
|
async function startCommandLoop(ctx) {
|
|
13490
13895
|
let heartbeatCount = 0;
|
|
13491
13896
|
const heartbeatTimer = setInterval(() => {
|
|
13492
|
-
ctx.
|
|
13897
|
+
ctx.deps.backend.mutation(api.machines.daemonHeartbeat, {
|
|
13493
13898
|
sessionId: ctx.sessionId,
|
|
13494
13899
|
machineId: ctx.machineId
|
|
13495
13900
|
}).then(() => {
|
|
@@ -13504,13 +13909,7 @@ async function startCommandLoop(ctx) {
|
|
|
13504
13909
|
console.log(`
|
|
13505
13910
|
[${formatTimestamp()}] Shutting down...`);
|
|
13506
13911
|
clearInterval(heartbeatTimer);
|
|
13507
|
-
|
|
13508
|
-
await ctx.client.mutation(api.machines.updateDaemonStatus, {
|
|
13509
|
-
sessionId: ctx.sessionId,
|
|
13510
|
-
machineId: ctx.machineId,
|
|
13511
|
-
connected: false
|
|
13512
|
-
});
|
|
13513
|
-
} catch {}
|
|
13912
|
+
await onDaemonShutdown(ctx);
|
|
13514
13913
|
releaseLock();
|
|
13515
13914
|
process.exit(0);
|
|
13516
13915
|
};
|
|
@@ -13566,7 +13965,7 @@ Listening for commands...`);
|
|
|
13566
13965
|
parsed.push(command);
|
|
13567
13966
|
} else {
|
|
13568
13967
|
try {
|
|
13569
|
-
await ctx.
|
|
13968
|
+
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
13570
13969
|
sessionId: ctx.sessionId,
|
|
13571
13970
|
commandId: raw._id,
|
|
13572
13971
|
status: "failed",
|
|
@@ -13587,20 +13986,283 @@ Listening for commands...`);
|
|
|
13587
13986
|
modelRefreshTimer.unref();
|
|
13588
13987
|
return await new Promise(() => {});
|
|
13589
13988
|
}
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
var init_daemon_start = __esm(() => {
|
|
13989
|
+
var MODEL_REFRESH_INTERVAL_MS;
|
|
13990
|
+
var init_command_loop = __esm(() => {
|
|
13991
|
+
init_api3();
|
|
13992
|
+
init_client2();
|
|
13993
|
+
init_on_daemon_shutdown();
|
|
13596
13994
|
init_pid();
|
|
13995
|
+
init_start_agent();
|
|
13996
|
+
init_stop_agent();
|
|
13997
|
+
MODEL_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
13998
|
+
});
|
|
13999
|
+
|
|
14000
|
+
// src/commands/machine/daemon-start/handlers/state-recovery.ts
|
|
14001
|
+
async function recoverAgentState(ctx) {
|
|
14002
|
+
const entries = ctx.deps.machine.listAgentEntries(ctx.machineId);
|
|
14003
|
+
if (entries.length === 0) {
|
|
14004
|
+
console.log(` No agent entries found — nothing to recover`);
|
|
14005
|
+
return;
|
|
14006
|
+
}
|
|
14007
|
+
let recovered = 0;
|
|
14008
|
+
let cleared = 0;
|
|
14009
|
+
for (const { chatroomId, role, entry } of entries) {
|
|
14010
|
+
const { pid, harness } = entry;
|
|
14011
|
+
const alive = ctx.remoteAgentService.isAlive(pid);
|
|
14012
|
+
if (alive) {
|
|
14013
|
+
console.log(` ✅ Recovered: ${role} (PID ${pid}, harness: ${harness})`);
|
|
14014
|
+
recovered++;
|
|
14015
|
+
} else {
|
|
14016
|
+
console.log(` \uD83E\uDDF9 Stale PID ${pid} for ${role} — clearing`);
|
|
14017
|
+
await clearAgentPidEverywhere(ctx, chatroomId, role);
|
|
14018
|
+
cleared++;
|
|
14019
|
+
}
|
|
14020
|
+
}
|
|
14021
|
+
console.log(` Recovery complete: ${recovered} alive, ${cleared} stale cleared`);
|
|
14022
|
+
}
|
|
14023
|
+
var init_state_recovery = __esm(() => {
|
|
14024
|
+
init_shared();
|
|
14025
|
+
});
|
|
14026
|
+
|
|
14027
|
+
// src/commands/machine/daemon-start/event-bus.ts
|
|
14028
|
+
class DaemonEventBus {
|
|
14029
|
+
listeners = new Map;
|
|
14030
|
+
on(event, listener) {
|
|
14031
|
+
if (!this.listeners.has(event)) {
|
|
14032
|
+
this.listeners.set(event, new Set);
|
|
14033
|
+
}
|
|
14034
|
+
this.listeners.get(event).add(listener);
|
|
14035
|
+
return () => {
|
|
14036
|
+
this.listeners.get(event)?.delete(listener);
|
|
14037
|
+
};
|
|
14038
|
+
}
|
|
14039
|
+
emit(event, payload) {
|
|
14040
|
+
const set = this.listeners.get(event);
|
|
14041
|
+
if (!set)
|
|
14042
|
+
return;
|
|
14043
|
+
for (const listener of set) {
|
|
14044
|
+
try {
|
|
14045
|
+
listener(payload);
|
|
14046
|
+
} catch (err) {
|
|
14047
|
+
console.warn(`[EventBus] Listener error on "${event}": ${err.message}`);
|
|
14048
|
+
}
|
|
14049
|
+
}
|
|
14050
|
+
}
|
|
14051
|
+
removeAllListeners() {
|
|
14052
|
+
this.listeners.clear();
|
|
14053
|
+
}
|
|
14054
|
+
}
|
|
14055
|
+
|
|
14056
|
+
// src/commands/machine/daemon-start/event-listeners.ts
|
|
14057
|
+
function registerEventListeners(ctx) {
|
|
14058
|
+
const unsubs = [];
|
|
14059
|
+
unsubs.push(ctx.events.on("agent:exited", (payload) => {
|
|
14060
|
+
const { chatroomId, role, pid, code: code2, signal, intentional } = payload;
|
|
14061
|
+
const ts = formatTimestamp();
|
|
14062
|
+
if (intentional) {
|
|
14063
|
+
console.log(`[${ts}] ℹ️ Agent process exited after intentional stop ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14064
|
+
} else {
|
|
14065
|
+
console.log(`[${ts}] ⚠️ Agent process exited ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14066
|
+
}
|
|
14067
|
+
ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
14068
|
+
sessionId: ctx.sessionId,
|
|
14069
|
+
machineId: ctx.machineId,
|
|
14070
|
+
chatroomId,
|
|
14071
|
+
role,
|
|
14072
|
+
pid: undefined
|
|
14073
|
+
}).catch((err) => {
|
|
14074
|
+
console.log(` ⚠️ Failed to clear PID in backend: ${err.message}`);
|
|
14075
|
+
});
|
|
14076
|
+
ctx.deps.machine.clearAgentPid(ctx.machineId, chatroomId, role);
|
|
14077
|
+
ctx.remoteAgentService.untrack(pid);
|
|
14078
|
+
ctx.deps.backend.mutation(api.participants.leave, {
|
|
14079
|
+
sessionId: ctx.sessionId,
|
|
14080
|
+
chatroomId,
|
|
14081
|
+
role
|
|
14082
|
+
}).catch((err) => {
|
|
14083
|
+
console.log(` ⚠️ Could not remove participant: ${err.message}`);
|
|
14084
|
+
});
|
|
14085
|
+
}));
|
|
14086
|
+
unsubs.push(ctx.events.on("agent:started", (payload) => {
|
|
14087
|
+
const ts = formatTimestamp();
|
|
14088
|
+
console.log(`[${ts}] \uD83D\uDFE2 Agent started: ${payload.role} (PID: ${payload.pid}, harness: ${payload.harness})`);
|
|
14089
|
+
}));
|
|
14090
|
+
unsubs.push(ctx.events.on("agent:stopped", (payload) => {
|
|
14091
|
+
const ts = formatTimestamp();
|
|
14092
|
+
console.log(`[${ts}] \uD83D\uDD34 Agent stopped: ${payload.role} (PID: ${payload.pid})`);
|
|
14093
|
+
}));
|
|
14094
|
+
return () => {
|
|
14095
|
+
for (const unsub of unsubs) {
|
|
14096
|
+
unsub();
|
|
14097
|
+
}
|
|
14098
|
+
};
|
|
14099
|
+
}
|
|
14100
|
+
var init_event_listeners = __esm(() => {
|
|
14101
|
+
init_api3();
|
|
14102
|
+
});
|
|
14103
|
+
|
|
14104
|
+
// src/commands/machine/daemon-start/init.ts
|
|
14105
|
+
import { stat } from "node:fs/promises";
|
|
14106
|
+
async function discoverModels(service) {
|
|
14107
|
+
try {
|
|
14108
|
+
return await service.listModels();
|
|
14109
|
+
} catch {
|
|
14110
|
+
return [];
|
|
14111
|
+
}
|
|
14112
|
+
}
|
|
14113
|
+
function createDefaultDeps16() {
|
|
14114
|
+
return {
|
|
14115
|
+
backend: {
|
|
14116
|
+
mutation: async () => {
|
|
14117
|
+
throw new Error("Backend not initialized");
|
|
14118
|
+
},
|
|
14119
|
+
query: async () => {
|
|
14120
|
+
throw new Error("Backend not initialized");
|
|
14121
|
+
}
|
|
14122
|
+
},
|
|
14123
|
+
processes: {
|
|
14124
|
+
kill: (pid, signal) => process.kill(pid, signal)
|
|
14125
|
+
},
|
|
14126
|
+
fs: {
|
|
14127
|
+
stat
|
|
14128
|
+
},
|
|
14129
|
+
stops: {
|
|
14130
|
+
mark: markIntentionalStop,
|
|
14131
|
+
consume: consumeIntentionalStop,
|
|
14132
|
+
clear: clearIntentionalStop
|
|
14133
|
+
},
|
|
14134
|
+
machine: {
|
|
14135
|
+
clearAgentPid,
|
|
14136
|
+
persistAgentPid,
|
|
14137
|
+
listAgentEntries
|
|
14138
|
+
},
|
|
14139
|
+
clock: {
|
|
14140
|
+
now: () => Date.now(),
|
|
14141
|
+
delay: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms))
|
|
14142
|
+
}
|
|
14143
|
+
};
|
|
14144
|
+
}
|
|
14145
|
+
async function initDaemon() {
|
|
14146
|
+
if (!acquireLock()) {
|
|
14147
|
+
process.exit(1);
|
|
14148
|
+
}
|
|
14149
|
+
const convexUrl = getConvexUrl();
|
|
14150
|
+
const sessionId = getSessionId();
|
|
14151
|
+
if (!sessionId) {
|
|
14152
|
+
const otherUrls = getOtherSessionUrls();
|
|
14153
|
+
console.error(`❌ Not authenticated for: ${convexUrl}`);
|
|
14154
|
+
if (otherUrls.length > 0) {
|
|
14155
|
+
console.error(`
|
|
14156
|
+
\uD83D\uDCA1 You have sessions for other environments:`);
|
|
14157
|
+
for (const url of otherUrls) {
|
|
14158
|
+
console.error(` • ${url}`);
|
|
14159
|
+
}
|
|
14160
|
+
}
|
|
14161
|
+
console.error(`
|
|
14162
|
+
Run: chatroom auth login`);
|
|
14163
|
+
releaseLock();
|
|
14164
|
+
process.exit(1);
|
|
14165
|
+
}
|
|
14166
|
+
const machineId = getMachineId();
|
|
14167
|
+
if (!machineId) {
|
|
14168
|
+
console.error(`❌ Machine not registered`);
|
|
14169
|
+
console.error(`
|
|
14170
|
+
Run any chatroom command first to register this machine,`);
|
|
14171
|
+
console.error(`for example: chatroom auth status`);
|
|
14172
|
+
releaseLock();
|
|
14173
|
+
process.exit(1);
|
|
14174
|
+
}
|
|
14175
|
+
const client2 = await getConvexClient();
|
|
14176
|
+
const typedSessionId = sessionId;
|
|
14177
|
+
const config3 = loadMachineConfig();
|
|
14178
|
+
const remoteAgentService = new OpenCodeAgentService;
|
|
14179
|
+
const availableModels = await discoverModels(remoteAgentService);
|
|
14180
|
+
if (config3) {
|
|
14181
|
+
try {
|
|
14182
|
+
await client2.mutation(api.machines.register, {
|
|
14183
|
+
sessionId: typedSessionId,
|
|
14184
|
+
machineId,
|
|
14185
|
+
hostname: config3.hostname,
|
|
14186
|
+
os: config3.os,
|
|
14187
|
+
availableHarnesses: config3.availableHarnesses,
|
|
14188
|
+
harnessVersions: config3.harnessVersions,
|
|
14189
|
+
availableModels
|
|
14190
|
+
});
|
|
14191
|
+
} catch (error) {
|
|
14192
|
+
console.warn(`⚠️ Machine registration update failed: ${error.message}`);
|
|
14193
|
+
}
|
|
14194
|
+
}
|
|
14195
|
+
try {
|
|
14196
|
+
await client2.mutation(api.machines.updateDaemonStatus, {
|
|
14197
|
+
sessionId: typedSessionId,
|
|
14198
|
+
machineId,
|
|
14199
|
+
connected: true
|
|
14200
|
+
});
|
|
14201
|
+
} catch (error) {
|
|
14202
|
+
if (isNetworkError(error)) {
|
|
14203
|
+
formatConnectivityError(error, convexUrl);
|
|
14204
|
+
} else {
|
|
14205
|
+
console.error(`❌ Failed to update daemon status: ${error.message}`);
|
|
14206
|
+
}
|
|
14207
|
+
releaseLock();
|
|
14208
|
+
process.exit(1);
|
|
14209
|
+
}
|
|
14210
|
+
const deps = createDefaultDeps16();
|
|
14211
|
+
deps.backend.mutation = (endpoint, args) => client2.mutation(endpoint, args);
|
|
14212
|
+
deps.backend.query = (endpoint, args) => client2.query(endpoint, args);
|
|
14213
|
+
const events = new DaemonEventBus;
|
|
14214
|
+
const ctx = {
|
|
14215
|
+
client: client2,
|
|
14216
|
+
sessionId: typedSessionId,
|
|
14217
|
+
machineId,
|
|
14218
|
+
config: config3,
|
|
14219
|
+
deps,
|
|
14220
|
+
events,
|
|
14221
|
+
remoteAgentService
|
|
14222
|
+
};
|
|
14223
|
+
registerEventListeners(ctx);
|
|
14224
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
|
|
14225
|
+
console.log(` CLI version: ${getVersion()}`);
|
|
14226
|
+
console.log(` Machine ID: ${machineId}`);
|
|
14227
|
+
console.log(` Hostname: ${config3?.hostname ?? "Unknown"}`);
|
|
14228
|
+
console.log(` Available harnesses: ${config3?.availableHarnesses.join(", ") || "none"}`);
|
|
14229
|
+
console.log(` Available models: ${availableModels.length > 0 ? `${availableModels.length} models` : "none discovered"}`);
|
|
14230
|
+
console.log(` PID: ${process.pid}`);
|
|
14231
|
+
console.log(`
|
|
14232
|
+
[${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
|
|
14233
|
+
try {
|
|
14234
|
+
await recoverAgentState(ctx);
|
|
14235
|
+
} catch (e) {
|
|
14236
|
+
console.log(` ⚠️ Recovery failed: ${e.message}`);
|
|
14237
|
+
console.log(` Continuing with fresh state`);
|
|
14238
|
+
}
|
|
14239
|
+
return ctx;
|
|
14240
|
+
}
|
|
14241
|
+
var init_init2 = __esm(() => {
|
|
14242
|
+
init_state_recovery();
|
|
13597
14243
|
init_api3();
|
|
13598
|
-
init_agent_drivers();
|
|
13599
14244
|
init_storage();
|
|
13600
14245
|
init_client2();
|
|
13601
14246
|
init_machine();
|
|
14247
|
+
init_intentional_stops();
|
|
14248
|
+
init_opencode();
|
|
14249
|
+
init_error_formatting();
|
|
13602
14250
|
init_version();
|
|
13603
|
-
|
|
14251
|
+
init_pid();
|
|
14252
|
+
init_event_listeners();
|
|
14253
|
+
});
|
|
14254
|
+
|
|
14255
|
+
// src/commands/machine/daemon-start/index.ts
|
|
14256
|
+
async function daemonStart() {
|
|
14257
|
+
const ctx = await initDaemon();
|
|
14258
|
+
await startCommandLoop(ctx);
|
|
14259
|
+
}
|
|
14260
|
+
var init_daemon_start = __esm(() => {
|
|
14261
|
+
init_command_loop();
|
|
14262
|
+
init_init2();
|
|
14263
|
+
init_start_agent();
|
|
14264
|
+
init_stop_agent();
|
|
14265
|
+
init_state_recovery();
|
|
13604
14266
|
});
|
|
13605
14267
|
|
|
13606
14268
|
// src/commands/machine/daemon-stop.ts
|
|
@@ -13613,7 +14275,7 @@ async function daemonStop() {
|
|
|
13613
14275
|
console.log(`Stopping daemon (PID: ${pid})...`);
|
|
13614
14276
|
try {
|
|
13615
14277
|
process.kill(pid, "SIGTERM");
|
|
13616
|
-
await new Promise((resolve2) => setTimeout(resolve2,
|
|
14278
|
+
await new Promise((resolve2) => setTimeout(resolve2, 8000));
|
|
13617
14279
|
try {
|
|
13618
14280
|
process.kill(pid, 0);
|
|
13619
14281
|
console.log(`Process did not exit gracefully, forcing...`);
|
|
@@ -13661,29 +14323,56 @@ var init_machine2 = __esm(() => {
|
|
|
13661
14323
|
init_daemon_status();
|
|
13662
14324
|
});
|
|
13663
14325
|
|
|
13664
|
-
// src/commands/opencode-install.ts
|
|
14326
|
+
// src/commands/opencode-install/index.ts
|
|
13665
14327
|
var exports_opencode_install = {};
|
|
13666
14328
|
__export(exports_opencode_install, {
|
|
13667
14329
|
installTool: () => installTool
|
|
13668
14330
|
});
|
|
13669
|
-
async function
|
|
14331
|
+
async function isChatroomInstalledDefault() {
|
|
13670
14332
|
try {
|
|
13671
|
-
const { execSync:
|
|
13672
|
-
|
|
14333
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
14334
|
+
execSync3("chatroom --version", { stdio: "pipe" });
|
|
13673
14335
|
return true;
|
|
13674
14336
|
} catch {
|
|
13675
14337
|
return false;
|
|
13676
14338
|
}
|
|
13677
14339
|
}
|
|
13678
|
-
async function
|
|
14340
|
+
async function createDefaultDeps17() {
|
|
14341
|
+
const client2 = await getConvexClient();
|
|
14342
|
+
const fs = await import("fs/promises");
|
|
14343
|
+
return {
|
|
14344
|
+
backend: {
|
|
14345
|
+
mutation: (endpoint, args) => client2.mutation(endpoint, args),
|
|
14346
|
+
query: (endpoint, args) => client2.query(endpoint, args)
|
|
14347
|
+
},
|
|
14348
|
+
session: {
|
|
14349
|
+
getSessionId,
|
|
14350
|
+
getConvexUrl,
|
|
14351
|
+
getOtherSessionUrls
|
|
14352
|
+
},
|
|
14353
|
+
fs: {
|
|
14354
|
+
access: async (p) => {
|
|
14355
|
+
await fs.access(p);
|
|
14356
|
+
},
|
|
14357
|
+
mkdir: async (p, options) => {
|
|
14358
|
+
await fs.mkdir(p, options);
|
|
14359
|
+
},
|
|
14360
|
+
writeFile: async (p, content, encoding) => {
|
|
14361
|
+
await fs.writeFile(p, content, encoding);
|
|
14362
|
+
}
|
|
14363
|
+
},
|
|
14364
|
+
isChatroomInstalled: isChatroomInstalledDefault
|
|
14365
|
+
};
|
|
14366
|
+
}
|
|
14367
|
+
async function installTool(options = {}, deps) {
|
|
14368
|
+
const d = deps ?? await createDefaultDeps17();
|
|
13679
14369
|
const { checkExisting = true } = options;
|
|
13680
14370
|
const os = await import("os");
|
|
13681
|
-
const
|
|
13682
|
-
const path = await import("path");
|
|
14371
|
+
const path2 = await import("path");
|
|
13683
14372
|
const homeDir = os.homedir();
|
|
13684
|
-
const toolDir =
|
|
13685
|
-
const toolPath =
|
|
13686
|
-
const handoffToolPath =
|
|
14373
|
+
const toolDir = path2.join(homeDir, ".config", "opencode", "tool");
|
|
14374
|
+
const toolPath = path2.join(toolDir, "chatroom.ts");
|
|
14375
|
+
const handoffToolPath = path2.join(toolDir, "chatroom-handoff.ts");
|
|
13687
14376
|
const toolContent = `import { tool } from "@opencode-ai/plugin";
|
|
13688
14377
|
|
|
13689
14378
|
/**
|
|
@@ -13713,7 +14402,7 @@ async function checkChatroomStatus(): Promise<{ installed: boolean; authenticate
|
|
|
13713
14402
|
|
|
13714
14403
|
export default tool({
|
|
13715
14404
|
description:
|
|
13716
|
-
"
|
|
14405
|
+
"Get next task in a multi-agent chatroom. This command joins a chatroom with a specific role and waits for tasks to be assigned. It's a long-running operation that polls for pending tasks and handles the complete workflow including authentication, task claiming, and graceful interruption handling. Use this instead of bash 'chatroom get-next-task' to avoid timeout issues.",
|
|
13717
14406
|
args: {
|
|
13718
14407
|
chatroomId: tool.schema
|
|
13719
14408
|
.string()
|
|
@@ -13773,7 +14462,7 @@ After logging in, try this command again.\`;
|
|
|
13773
14462
|
}
|
|
13774
14463
|
|
|
13775
14464
|
// Build command arguments
|
|
13776
|
-
const cmdArgs = ['
|
|
14465
|
+
const cmdArgs = ['get-next-task', args.chatroomId, '--role', args.role];
|
|
13777
14466
|
|
|
13778
14467
|
if (args.duration !== undefined) {
|
|
13779
14468
|
cmdArgs.push('--duration', args.duration);
|
|
@@ -13790,7 +14479,7 @@ After logging in, try this command again.\`;
|
|
|
13790
14479
|
env.CHATROOM_CONVEX_URL = args.convexUrl;
|
|
13791
14480
|
}
|
|
13792
14481
|
|
|
13793
|
-
// Execute the
|
|
14482
|
+
// Execute the get-next-task command
|
|
13794
14483
|
// This is a long-running operation that polls for tasks
|
|
13795
14484
|
const proc = Bun.spawn(['chatroom', ...cmdArgs], {
|
|
13796
14485
|
stdout: 'pipe',
|
|
@@ -13967,11 +14656,11 @@ After logging in, try this command again.\`;
|
|
|
13967
14656
|
if (checkExisting) {
|
|
13968
14657
|
const existingFiles = [];
|
|
13969
14658
|
try {
|
|
13970
|
-
await fs.access(toolPath);
|
|
14659
|
+
await d.fs.access(toolPath);
|
|
13971
14660
|
existingFiles.push(toolPath);
|
|
13972
14661
|
} catch {}
|
|
13973
14662
|
try {
|
|
13974
|
-
await fs.access(handoffToolPath);
|
|
14663
|
+
await d.fs.access(handoffToolPath);
|
|
13975
14664
|
existingFiles.push(handoffToolPath);
|
|
13976
14665
|
} catch {}
|
|
13977
14666
|
if (existingFiles.length > 0) {
|
|
@@ -13991,7 +14680,7 @@ Then run this command again, or use --force to overwrite.`;
|
|
|
13991
14680
|
};
|
|
13992
14681
|
}
|
|
13993
14682
|
}
|
|
13994
|
-
const installed = await isChatroomInstalled();
|
|
14683
|
+
const installed = await d.isChatroomInstalled();
|
|
13995
14684
|
if (!installed) {
|
|
13996
14685
|
const message2 = `⚠️ Chatroom CLI is not installed.
|
|
13997
14686
|
|
|
@@ -14005,9 +14694,9 @@ After installation, run this command again.`;
|
|
|
14005
14694
|
message: message2
|
|
14006
14695
|
};
|
|
14007
14696
|
}
|
|
14008
|
-
await fs.mkdir(toolDir, { recursive: true });
|
|
14009
|
-
await fs.writeFile(toolPath, toolContent, "utf-8");
|
|
14010
|
-
await fs.writeFile(handoffToolPath, handoffToolContent, "utf-8");
|
|
14697
|
+
await d.fs.mkdir(toolDir, { recursive: true });
|
|
14698
|
+
await d.fs.writeFile(toolPath, toolContent, "utf-8");
|
|
14699
|
+
await d.fs.writeFile(handoffToolPath, handoffToolContent, "utf-8");
|
|
14011
14700
|
const message = `✅ Installed chatroom OpenCode tools successfully!
|
|
14012
14701
|
|
|
14013
14702
|
Locations:
|
|
@@ -14015,7 +14704,7 @@ Locations:
|
|
|
14015
14704
|
• ${handoffToolPath}
|
|
14016
14705
|
|
|
14017
14706
|
The following commands are now available in OpenCode:
|
|
14018
|
-
• chatroom (
|
|
14707
|
+
• chatroom (get-next-task) - Get next task from chatroom (no more timeouts!)
|
|
14019
14708
|
• chatroom-handoff - Complete your task and hand off to the next role
|
|
14020
14709
|
|
|
14021
14710
|
Both tools will automatically check for:
|
|
@@ -14043,8 +14732,56 @@ If you're not authenticated, run:
|
|
|
14043
14732
|
};
|
|
14044
14733
|
}
|
|
14045
14734
|
}
|
|
14735
|
+
var init_opencode_install = __esm(() => {
|
|
14736
|
+
init_storage();
|
|
14737
|
+
init_client2();
|
|
14738
|
+
});
|
|
14739
|
+
|
|
14740
|
+
// src/infrastructure/retry-queue.ts
|
|
14741
|
+
async function withRetry(fn, opts) {
|
|
14742
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = { ...DEFAULTS, ...opts };
|
|
14743
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
14744
|
+
try {
|
|
14745
|
+
return await fn();
|
|
14746
|
+
} catch (e) {
|
|
14747
|
+
if (attempt === maxRetries) {
|
|
14748
|
+
console.warn(`[retry-queue] All ${maxRetries + 1} attempts exhausted: ${e.message}`);
|
|
14749
|
+
return;
|
|
14750
|
+
}
|
|
14751
|
+
const delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
14752
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
14753
|
+
}
|
|
14754
|
+
}
|
|
14755
|
+
return;
|
|
14756
|
+
}
|
|
14757
|
+
var DEFAULTS;
|
|
14758
|
+
var init_retry_queue = __esm(() => {
|
|
14759
|
+
DEFAULTS = {
|
|
14760
|
+
maxRetries: 3,
|
|
14761
|
+
baseDelayMs: 500,
|
|
14762
|
+
maxDelayMs: 5000
|
|
14763
|
+
};
|
|
14764
|
+
});
|
|
14765
|
+
|
|
14766
|
+
// src/infrastructure/lifecycle-heartbeat.ts
|
|
14767
|
+
var exports_lifecycle_heartbeat = {};
|
|
14768
|
+
__export(exports_lifecycle_heartbeat, {
|
|
14769
|
+
sendLifecycleHeartbeat: () => sendLifecycleHeartbeat
|
|
14770
|
+
});
|
|
14771
|
+
function sendLifecycleHeartbeat(client2, opts) {
|
|
14772
|
+
withRetry(() => client2.mutation(api.participants.join, {
|
|
14773
|
+
sessionId: opts.sessionId,
|
|
14774
|
+
chatroomId: opts.chatroomId,
|
|
14775
|
+
role: opts.role,
|
|
14776
|
+
...opts.action !== undefined ? { action: opts.action } : {}
|
|
14777
|
+
})).catch(() => {});
|
|
14778
|
+
}
|
|
14779
|
+
var init_lifecycle_heartbeat = __esm(() => {
|
|
14780
|
+
init_api3();
|
|
14781
|
+
init_retry_queue();
|
|
14782
|
+
});
|
|
14046
14783
|
|
|
14047
|
-
// ../../node_modules/.pnpm/commander@14.0.
|
|
14784
|
+
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
|
|
14048
14785
|
var import__ = __toESM(require_commander(), 1);
|
|
14049
14786
|
var {
|
|
14050
14787
|
program,
|
|
@@ -14089,8 +14826,8 @@ async function maybeRequireAuth() {
|
|
|
14089
14826
|
console.log("⚠️ Skipping authentication (--skip-auth flag)");
|
|
14090
14827
|
return;
|
|
14091
14828
|
}
|
|
14092
|
-
const { requireAuth:
|
|
14093
|
-
await
|
|
14829
|
+
const { requireAuth: requireAuth3 } = await Promise.resolve().then(() => (init_middleware(), exports_middleware));
|
|
14830
|
+
await requireAuth3();
|
|
14094
14831
|
}
|
|
14095
14832
|
var authCommand = program2.command("auth").description("Manage CLI authentication");
|
|
14096
14833
|
authCommand.command("login").description("Authenticate the CLI via browser").option("-f, --force", "Re-authenticate even if already logged in").action(async (options) => {
|
|
@@ -14109,6 +14846,10 @@ program2.command("update").description("Update the CLI to the latest version").a
|
|
|
14109
14846
|
const { update: update2 } = await Promise.resolve().then(() => (init_update(), exports_update));
|
|
14110
14847
|
await update2();
|
|
14111
14848
|
});
|
|
14849
|
+
program2.command("init").description("Initialize chatroom integration in your project").option("--dir <path>", "Directory to initialize (default: current directory)").action(async (options) => {
|
|
14850
|
+
const { init: init2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
14851
|
+
await init2({ dir: options.dir });
|
|
14852
|
+
});
|
|
14112
14853
|
program2.command("register-agent").description("Register agent type for a chatroom role").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Role to register as (e.g., builder, reviewer)").requiredOption("--type <type>", "Agent type: remote or custom").action(async (options) => {
|
|
14113
14854
|
await maybeRequireAuth();
|
|
14114
14855
|
if (options.type !== "remote" && options.type !== "custom") {
|
|
@@ -14121,10 +14862,10 @@ program2.command("register-agent").description("Register agent type for a chatro
|
|
|
14121
14862
|
type: options.type
|
|
14122
14863
|
});
|
|
14123
14864
|
});
|
|
14124
|
-
program2.command("
|
|
14865
|
+
program2.command("get-next-task").description("Join a chatroom and get the next task").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Role to join as (e.g., builder, reviewer)").action(async (options) => {
|
|
14125
14866
|
await maybeRequireAuth();
|
|
14126
|
-
const {
|
|
14127
|
-
await
|
|
14867
|
+
const { getNextTask: getNextTask2 } = await Promise.resolve().then(() => (init_get_next_task(), exports_get_next_task));
|
|
14868
|
+
await getNextTask2(options.chatroomId, {
|
|
14128
14869
|
role: options.role
|
|
14129
14870
|
});
|
|
14130
14871
|
});
|
|
@@ -14177,13 +14918,6 @@ program2.command("task-started").description("Acknowledge a task and optionally
|
|
|
14177
14918
|
noClassify: skipClassification
|
|
14178
14919
|
});
|
|
14179
14920
|
});
|
|
14180
|
-
program2.command("task-complete").description("Complete the current task without handing off to another role").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").action(async (options) => {
|
|
14181
|
-
await maybeRequireAuth();
|
|
14182
|
-
const { taskComplete: taskComplete2 } = await Promise.resolve().then(() => (init_task_complete(), exports_task_complete));
|
|
14183
|
-
await taskComplete2(options.chatroomId, {
|
|
14184
|
-
role: options.role
|
|
14185
|
-
});
|
|
14186
|
-
});
|
|
14187
14921
|
program2.command("handoff").description("Complete your task and hand off to the next role").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").requiredOption("--next-role <nextRole>", "Role to hand off to").option("--attach-artifact <artifactId>", "Attach artifact to handoff (can be used multiple times)", (value, previous) => {
|
|
14188
14922
|
return previous ? [...previous, value] : [value];
|
|
14189
14923
|
}, []).action(async (options) => {
|
|
@@ -14292,11 +15026,6 @@ backlogCommand.command("score").description("Score a backlog task by complexity,
|
|
|
14292
15026
|
const { scoreBacklog: scoreBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
|
|
14293
15027
|
await scoreBacklog2(options.chatroomId, options);
|
|
14294
15028
|
});
|
|
14295
|
-
backlogCommand.command("reset-task").description("Reset a stuck in_progress task back to pending").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").requiredOption("--task-id <taskId>", "Task ID to reset").action(async (options) => {
|
|
14296
|
-
await maybeRequireAuth();
|
|
14297
|
-
const { resetBacklog: resetBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
|
|
14298
|
-
await resetBacklog2(options.chatroomId, options);
|
|
14299
|
-
});
|
|
14300
15029
|
backlogCommand.command("mark-for-review").description("Mark a backlog task as ready for user review (backlog → pending_user_review)").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").requiredOption("--task-id <taskId>", "Task ID to mark for review").action(async (options) => {
|
|
14301
15030
|
await maybeRequireAuth();
|
|
14302
15031
|
const { markForReviewBacklog: markForReviewBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
|
|
@@ -14336,7 +15065,7 @@ contextCommand.command("read").description("Read context for your role (conversa
|
|
|
14336
15065
|
const { readContext: readContext2 } = await Promise.resolve().then(() => (init_context(), exports_context));
|
|
14337
15066
|
await readContext2(options.chatroomId, options);
|
|
14338
15067
|
});
|
|
14339
|
-
contextCommand.command("new").description("Create a new context and pin it for all agents").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role (creator of the context)").option("--content <content>", "Context summary/description (alternative: provide via stdin/heredoc)").action(async (options) => {
|
|
15068
|
+
contextCommand.command("new").description("Create a new context and pin it for all agents").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role (creator of the context)").option("--content <content>", "Context summary/description (alternative: provide via stdin/heredoc)").option("--trigger-message-id <messageId>", "Message ID that triggered this context (anchors the context window)").action(async (options) => {
|
|
14340
15069
|
await maybeRequireAuth();
|
|
14341
15070
|
let content;
|
|
14342
15071
|
if (options.content && options.content.trim().length > 0) {
|
|
@@ -14401,6 +15130,11 @@ artifactCommand.command("view-many").description("View multiple artifacts").requ
|
|
|
14401
15130
|
artifactIds: options.artifact || []
|
|
14402
15131
|
});
|
|
14403
15132
|
});
|
|
15133
|
+
program2.command("get-system-prompt").description("Fetch the system prompt for your role in a chatroom").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role (e.g., planner, builder, reviewer)").action(async (options) => {
|
|
15134
|
+
await maybeRequireAuth();
|
|
15135
|
+
const { getSystemPrompt: getSystemPrompt2 } = await Promise.resolve().then(() => (init_get_system_prompt(), exports_get_system_prompt));
|
|
15136
|
+
await getSystemPrompt2(options.chatroomId, { role: options.role });
|
|
15137
|
+
});
|
|
14404
15138
|
var machineCommand = program2.command("machine").description("Machine daemon management for remote agent control");
|
|
14405
15139
|
var daemonCommand = machineCommand.command("daemon").description("Manage the machine daemon");
|
|
14406
15140
|
daemonCommand.command("start").description("Start the machine daemon to listen for remote commands").action(async () => {
|
|
@@ -14418,7 +15152,22 @@ daemonCommand.command("status").description("Check if the machine daemon is runn
|
|
|
14418
15152
|
});
|
|
14419
15153
|
var opencodeCommand = program2.command("opencode").description("OpenCode integration harness");
|
|
14420
15154
|
opencodeCommand.command("install").description("Install chatroom as an OpenCode harness").option("--force", "Overwrite existing harness installation").action(async (options) => {
|
|
14421
|
-
const { installTool: installTool2 } = await Promise.resolve().then(() => exports_opencode_install);
|
|
15155
|
+
const { installTool: installTool2 } = await Promise.resolve().then(() => (init_opencode_install(), exports_opencode_install));
|
|
14422
15156
|
await installTool2({ checkExisting: !options.force });
|
|
14423
15157
|
});
|
|
15158
|
+
program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
15159
|
+
const opts = actionCommand.opts();
|
|
15160
|
+
const chatroomId = opts["chatroomId"];
|
|
15161
|
+
const role = opts["role"];
|
|
15162
|
+
if (!chatroomId || !role)
|
|
15163
|
+
return;
|
|
15164
|
+
const { getSessionId: getSessionId2 } = await Promise.resolve().then(() => (init_storage(), exports_storage));
|
|
15165
|
+
const { getConvexClient: getConvexClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
15166
|
+
const { sendLifecycleHeartbeat: sendLifecycleHeartbeat2 } = await Promise.resolve().then(() => (init_lifecycle_heartbeat(), exports_lifecycle_heartbeat));
|
|
15167
|
+
const sessionId = getSessionId2();
|
|
15168
|
+
if (!sessionId)
|
|
15169
|
+
return;
|
|
15170
|
+
const client2 = await getConvexClient2();
|
|
15171
|
+
sendLifecycleHeartbeat2(client2, { sessionId, chatroomId, role, action: actionCommand.name() });
|
|
15172
|
+
});
|
|
14424
15173
|
program2.parse();
|