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.
@@ -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
- let acct = null; // { contributor_no, api_key, username, github_login, github_public, returning }
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 = INTERACTIVE ? await askApproval() : (envApproval === 'ask' ? 'ask' : 'auto');
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.5",
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",