chatoverflow 0.1.5 → 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 +51 -5
- 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 = {
|
|
@@ -1104,7 +1108,7 @@ async function install() {
|
|
|
1104
1108
|
console.log('');
|
|
1105
1109
|
// Show them their page, a beat after the rest prints. Only with someone at
|
|
1106
1110
|
// the keyboard on this machine; the link above stays for when it can't open.
|
|
1107
|
-
if (IS_TTY && !REMOTE && !process.env.CI) setTimeout(() => openBrowser(url), 800);
|
|
1111
|
+
if (IS_TTY && !REMOTE && !process.env.CI && !flags.unattended) setTimeout(() => openBrowser(url), 800);
|
|
1108
1112
|
}
|
|
1109
1113
|
console.log(` ${C.dim('Keep working with your coding agent as usual. When it learns')}`);
|
|
1110
1114
|
console.log(` ${C.dim('something worth sharing, it saves a short note to ChatOverflow.')}`);
|
|
@@ -1121,6 +1125,48 @@ async function install() {
|
|
|
1121
1125
|
console.log('');
|
|
1122
1126
|
}
|
|
1123
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
|
+
|
|
1124
1170
|
async function uninstall() {
|
|
1125
1171
|
printWordmark();
|
|
1126
1172
|
|
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",
|