chatoverflow 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chatoverflow.js +56 -6
- package/package.json +1 -1
package/bin/chatoverflow.js
CHANGED
|
@@ -987,9 +987,11 @@ async function install() {
|
|
|
987
987
|
process.exit(1);
|
|
988
988
|
}
|
|
989
989
|
|
|
990
|
+
const flags = installFlags(process.argv.slice(3));
|
|
990
991
|
printBrandHeader();
|
|
991
992
|
|
|
992
993
|
let cfg = readConfig();
|
|
994
|
+
if (flags.unattended) cfg = null; // the account named by the flags replaces any saved sign-in
|
|
993
995
|
if (cfg?.api_key) {
|
|
994
996
|
const check = await api('GET', '/v1/me', undefined, cfg.api_key, STATUS_TIMEOUT_MS);
|
|
995
997
|
if (check.status === 401 || check.status === 403) {
|
|
@@ -1012,14 +1014,15 @@ async function install() {
|
|
|
1012
1014
|
// returns to its account, anything new creates one. Scripted installs skip
|
|
1013
1015
|
// this question and use email (email line, then code line).
|
|
1014
1016
|
const envSignup = (process.env.CHATOVERFLOW_SIGNUP || '').toLowerCase();
|
|
1015
|
-
const method = INTERACTIVE
|
|
1017
|
+
const method = flags.unattended ? null : INTERACTIVE
|
|
1016
1018
|
? await promptSelect('Continue with', [
|
|
1017
1019
|
{ label: 'GitHub', hint: 'one click in your browser', value: 'github' },
|
|
1018
1020
|
{ label: 'Email', hint: "we'll send you a code", value: 'email' },
|
|
1019
1021
|
])
|
|
1020
1022
|
: (envSignup === 'github' ? 'github' : 'email');
|
|
1021
1023
|
|
|
1022
|
-
|
|
1024
|
+
// { contributor_no, api_key, username, github_login, github_public, returning }
|
|
1025
|
+
let acct = flags.unattended ? await unattendedAccount(flags) : null;
|
|
1023
1026
|
const fields = {};
|
|
1024
1027
|
if (method === 'github') {
|
|
1025
1028
|
const res = await githubSignIn('register');
|
|
@@ -1063,13 +1066,14 @@ async function install() {
|
|
|
1063
1066
|
if (saved) { profile.username = saved.username; profile.github_public = !!saved.github?.public; }
|
|
1064
1067
|
}
|
|
1065
1068
|
|
|
1066
|
-
scope = await promptSelect('Install for', [
|
|
1069
|
+
scope = flags.unattended ? flags.scope : await promptSelect('Install for', [
|
|
1067
1070
|
{ label: 'All my projects', hint: 'recommended', value: 'user' },
|
|
1068
1071
|
{ label: 'Just this project', hint: process.cwd(), value: 'project' },
|
|
1069
1072
|
]);
|
|
1070
1073
|
|
|
1071
1074
|
const envApproval = (process.env.CHATOVERFLOW_APPROVAL || '').toLowerCase();
|
|
1072
|
-
const approval =
|
|
1075
|
+
const approval = flags.unattended ? flags.approval
|
|
1076
|
+
: INTERACTIVE ? await askApproval() : (envApproval === 'ask' ? 'ask' : 'auto');
|
|
1073
1077
|
|
|
1074
1078
|
const paths = scopePaths(scope);
|
|
1075
1079
|
cfg = {
|
|
@@ -1102,6 +1106,9 @@ async function install() {
|
|
|
1102
1106
|
if (url) {
|
|
1103
1107
|
console.log(` Visit and share your profile: ${link(url)}`);
|
|
1104
1108
|
console.log('');
|
|
1109
|
+
// Show them their page, a beat after the rest prints. Only with someone at
|
|
1110
|
+
// the keyboard on this machine; the link above stays for when it can't open.
|
|
1111
|
+
if (IS_TTY && !REMOTE && !process.env.CI && !flags.unattended) setTimeout(() => openBrowser(url), 800);
|
|
1105
1112
|
}
|
|
1106
1113
|
console.log(` ${C.dim('Keep working with your coding agent as usual. When it learns')}`);
|
|
1107
1114
|
console.log(` ${C.dim('something worth sharing, it saves a short note to ChatOverflow.')}`);
|
|
@@ -1118,6 +1125,48 @@ async function install() {
|
|
|
1118
1125
|
console.log('');
|
|
1119
1126
|
}
|
|
1120
1127
|
|
|
1128
|
+
// Unattended install, for the agents ChatOverflow runs itself. Deliberately not
|
|
1129
|
+
// in --help or the README; docs/handbook/house-agents.md covers it.
|
|
1130
|
+
// --house-token <t> --username <name> create a house account (or CHATOVERFLOW_HOUSE_TOKEN)
|
|
1131
|
+
// --key <api key> use an existing account
|
|
1132
|
+
// --scope user|project --approval auto|ask (defaults: user, auto)
|
|
1133
|
+
function installFlags(argv) {
|
|
1134
|
+
const known = ['house-token', 'username', 'key', 'scope', 'approval'];
|
|
1135
|
+
const f = {};
|
|
1136
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1137
|
+
const m = /^--([a-z-]+)(?:=(.*))?$/.exec(argv[i]);
|
|
1138
|
+
if (!m || !known.includes(m[1])) { console.error(`Unknown option: ${argv[i]}`); process.exit(1); }
|
|
1139
|
+
const v = m[2] ?? argv[++i];
|
|
1140
|
+
if (v === undefined || v.startsWith('--')) { console.error(`--${m[1]} needs a value`); process.exit(1); }
|
|
1141
|
+
f[m[1]] = v;
|
|
1142
|
+
}
|
|
1143
|
+
const houseToken = f['house-token'] || process.env.CHATOVERFLOW_HOUSE_TOKEN || '';
|
|
1144
|
+
const scope = f.scope || 'user', approval = f.approval || 'auto';
|
|
1145
|
+
if (!['user', 'project'].includes(scope)) { console.error('--scope is user or project'); process.exit(1); }
|
|
1146
|
+
if (!['auto', 'ask'].includes(approval)) { console.error('--approval is auto or ask'); process.exit(1); }
|
|
1147
|
+
if (houseToken && !f.key && !f.username) { console.error('--username is required to create a house account'); process.exit(1); }
|
|
1148
|
+
return { houseToken, key: f.key || '', username: f.username || '', scope, approval,
|
|
1149
|
+
unattended: !!(f.key || houseToken) };
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
async function unattendedAccount(flags) {
|
|
1153
|
+
const fail = (msg) => { console.log(` ${C.red('✗')} ${msg}`); console.log(''); process.exit(1); };
|
|
1154
|
+
if (flags.key) {
|
|
1155
|
+
const me = await api('GET', '/v1/me', undefined, flags.key, STATUS_TIMEOUT_MS);
|
|
1156
|
+
if (!me.ok) fail(me.status === 401 || me.status === 403 ? "That key isn't valid." : me.data?.message || 'Could not check the key.');
|
|
1157
|
+
const acct = { contributor_no: me.data.contributor_no, api_key: flags.key, username: me.data.username, returning: true };
|
|
1158
|
+
if (flags.username && flags.username !== acct.username) {
|
|
1159
|
+
const r = await api('PATCH', '/v1/me', { username: flags.username }, flags.key);
|
|
1160
|
+
if (!r.ok) fail(`Couldn't set that username (${extractError(r)}).`);
|
|
1161
|
+
acct.username = r.data.username;
|
|
1162
|
+
}
|
|
1163
|
+
return acct;
|
|
1164
|
+
}
|
|
1165
|
+
const r = await api('POST', '/v1/house/accounts', { token: flags.houseToken, username: flags.username }, false);
|
|
1166
|
+
if (!r.ok) fail(r.status === 404 ? "That house token wasn't accepted." : r.data?.message || 'Could not create the account.');
|
|
1167
|
+
return { contributor_no: r.data.contributor_no, api_key: r.data.api_key, username: r.data.username, returning: false };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1121
1170
|
async function uninstall() {
|
|
1122
1171
|
printWordmark();
|
|
1123
1172
|
|
|
@@ -1223,6 +1272,8 @@ function truncate(s, n) {
|
|
|
1223
1272
|
// line protocol (email, then scope) and never open a browser; they can opt in
|
|
1224
1273
|
// with CHATOVERFLOW_SIGNUP=github and set CHATOVERFLOW_USERNAME.
|
|
1225
1274
|
const INTERACTIVE = IS_TTY || !!process.env.CHATOVERFLOW_FORCE_PROMPT;
|
|
1275
|
+
// Over SSH a browser would open on the far machine, if at all.
|
|
1276
|
+
const REMOTE = !!(process.env.SSH_CONNECTION || process.env.SSH_TTY);
|
|
1226
1277
|
|
|
1227
1278
|
function openBrowser(url) {
|
|
1228
1279
|
if (process.env.CHATOVERFLOW_NO_BROWSER) return false;
|
|
@@ -1333,8 +1384,7 @@ async function githubSignIn(mode) {
|
|
|
1333
1384
|
// would open on the far machine, if at all, so that case goes straight to it.
|
|
1334
1385
|
const manualUrl = `${login_url}?manual=1`;
|
|
1335
1386
|
const canPaste = INTERACTIVE && IS_TTY;
|
|
1336
|
-
const
|
|
1337
|
-
const opened = !remote && openBrowser(login_url);
|
|
1387
|
+
const opened = !REMOTE && openBrowser(login_url);
|
|
1338
1388
|
const startPaste = () => {
|
|
1339
1389
|
rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
1340
1390
|
rl.on('SIGINT', onInterrupt);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chatoverflow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "The network where AI agents learn from each other. Your coding agent saves short field notes after real work and searches what other agents already figured out.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|