@brianli/kimaki 0.4.73-brianli.2 → 0.4.73-brianli.3

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.
Files changed (3) hide show
  1. package/dist/cli.js +66 -58
  2. package/package.json +4 -4
  3. package/src/cli.ts +81 -72
package/dist/cli.js CHANGED
@@ -914,70 +914,73 @@ async function run({ restartOnboarding, addChannels, useWorktrees, enableVoiceCh
914
914
  const clientId = KIMAKI_CLIENT_ID || crypto.randomUUID();
915
915
  const clientSecret = KIMAKI_CLIENT_SECRET
916
916
  || crypto.randomBytes(32).toString('hex');
917
- if (KIMAKI_CLIENT_ID && KIMAKI_CLIENT_SECRET) {
918
- cliLogger.log('Using KIMAKI_CLIENT_ID and KIMAKI_CLIENT_SECRET from environment for gateway mode.');
919
- }
920
- const statePayload = JSON.stringify({ clientId, clientSecret });
921
- const oauthUrl = generateBotInstallUrl({
922
- clientId: KIMAKI_SHARED_APP_ID,
923
- state: statePayload,
924
- redirectUri: `${KIMAKI_WEBSITE_URL}/api/auth/callback/discord`,
925
- responseType: 'code',
926
- });
927
- note(`Open this URL to install the Kimaki bot in your Discord server:\n\n${oauthUrl}\n\nDo not share this URL with anyone — it contains your credentials.\n\nIf you don't have a server, create one first (+ button in the Discord sidebar).`, 'Install Bot');
928
- // Open URL in default browser
929
- const { exec } = await import('node:child_process');
930
- const openCmd = process.platform === 'darwin'
931
- ? 'open'
932
- : process.platform === 'win32'
933
- ? 'start'
934
- : 'xdg-open';
935
- exec(`${openCmd} "${oauthUrl}"`);
936
- // Poll until the user installs the bot in a Discord server.
937
- // 600 attempts x 2s = 20 minutes timeout.
938
- const s = spinner();
939
- s.start('Waiting for a Discord server with the bot installed...');
940
- const pollUrl = new URL('/api/onboarding/status', KIMAKI_WEBSITE_URL);
941
- pollUrl.searchParams.set('client_id', clientId);
942
- pollUrl.searchParams.set('secret', clientSecret);
943
- let guildId;
944
- for (let attempt = 0; attempt < 600; attempt++) {
945
- await new Promise((resolve) => {
946
- setTimeout(resolve, 2000);
917
+ const hasGatewayEnvCredentials = hasGatewayClientId && hasGatewayClientSecret;
918
+ if (hasGatewayEnvCredentials) {
919
+ cliLogger.log('Using KIMAKI_CLIENT_ID and KIMAKI_CLIENT_SECRET from environment for gateway mode. Skipping OAuth onboarding.');
920
+ }
921
+ if (!hasGatewayEnvCredentials) {
922
+ const statePayload = JSON.stringify({ clientId, clientSecret });
923
+ const oauthUrl = generateBotInstallUrl({
924
+ clientId: KIMAKI_SHARED_APP_ID,
925
+ state: statePayload,
926
+ redirectUri: `${KIMAKI_WEBSITE_URL}/api/auth/callback/discord`,
927
+ responseType: 'code',
947
928
  });
948
- // Progressive hints for users who may be stuck
949
- if (attempt === 15) {
950
- // ~30s
951
- s.message('Still waiting... Select a server in the Discord authorization page and click "Authorize"');
952
- }
953
- else if (attempt === 45) {
954
- // ~90s
955
- s.message(`Still waiting... If you don't see any servers, create one first (+ button in Discord sidebar), then reopen the URL above`);
956
- }
957
- else if (attempt === 150) {
958
- // ~5min
959
- s.message(`Still waiting... Reopen the install URL if you closed it:\n${oauthUrl}`);
960
- }
961
- try {
962
- const resp = await fetch(pollUrl.toString());
963
- if (resp.ok) {
964
- const data = (await resp.json());
965
- if (data.guild_id) {
966
- guildId = data.guild_id;
967
- break;
929
+ note(`Open this URL to install the Kimaki bot in your Discord server:\n\n${oauthUrl}\n\nDo not share this URL with anyone — it contains your credentials.\n\nIf you don't have a server, create one first (+ button in the Discord sidebar).`, 'Install Bot');
930
+ // Open URL in default browser
931
+ const { exec } = await import('node:child_process');
932
+ const openCmd = process.platform === 'darwin'
933
+ ? 'open'
934
+ : process.platform === 'win32'
935
+ ? 'start'
936
+ : 'xdg-open';
937
+ exec(`${openCmd} "${oauthUrl}"`);
938
+ // Poll until the user installs the bot in a Discord server.
939
+ // 600 attempts x 2s = 20 minutes timeout.
940
+ const s = spinner();
941
+ s.start('Waiting for a Discord server with the bot installed...');
942
+ const pollUrl = new URL('/api/onboarding/status', KIMAKI_WEBSITE_URL);
943
+ pollUrl.searchParams.set('client_id', clientId);
944
+ pollUrl.searchParams.set('secret', clientSecret);
945
+ let guildId;
946
+ for (let attempt = 0; attempt < 600; attempt++) {
947
+ await new Promise((resolve) => {
948
+ setTimeout(resolve, 2000);
949
+ });
950
+ // Progressive hints for users who may be stuck
951
+ if (attempt === 15) {
952
+ // ~30s
953
+ s.message('Still waiting... Select a server in the Discord authorization page and click "Authorize"');
954
+ }
955
+ else if (attempt === 45) {
956
+ // ~90s
957
+ s.message(`Still waiting... If you don't see any servers, create one first (+ button in Discord sidebar), then reopen the URL above`);
958
+ }
959
+ else if (attempt === 150) {
960
+ // ~5min
961
+ s.message(`Still waiting... Reopen the install URL if you closed it:\n${oauthUrl}`);
962
+ }
963
+ try {
964
+ const resp = await fetch(pollUrl.toString());
965
+ if (resp.ok) {
966
+ const data = (await resp.json());
967
+ if (data.guild_id) {
968
+ guildId = data.guild_id;
969
+ break;
970
+ }
968
971
  }
969
972
  }
973
+ catch {
974
+ // Network error, retry
975
+ }
970
976
  }
971
- catch {
972
- // Network error, retry
977
+ if (!guildId) {
978
+ s.stop('Authorization timed out');
979
+ cliLogger.error('Bot authorization timed out after 20 minutes. Please try again.');
980
+ process.exit(EXIT_NO_RESTART);
973
981
  }
982
+ s.stop('Bot authorized successfully!');
974
983
  }
975
- if (!guildId) {
976
- s.stop('Authorization timed out');
977
- cliLogger.error('Bot authorization timed out after 20 minutes. Please try again.');
978
- process.exit(EXIT_NO_RESTART);
979
- }
980
- s.stop('Bot authorized successfully!');
981
984
  await setBotMode({
982
985
  appId: KIMAKI_SHARED_APP_ID,
983
986
  mode: 'gateway',
@@ -1067,6 +1070,11 @@ async function run({ restartOnboarding, addChannels, useWorktrees, enableVoiceCh
1067
1070
  // ensures they always get the bot that's actually running.
1068
1071
  await touchBotTokenTimestamp(appId);
1069
1072
  const shouldAddChannels = !isQuickStart || forceRestartOnboarding || Boolean(addChannels);
1073
+ store.setState({
1074
+ discordBaseUrl: isGatewayMode
1075
+ ? KIMAKI_GATEWAY_PROXY_REST_BASE_URL
1076
+ : 'https://discord.com',
1077
+ });
1070
1078
  // Start OpenCode server EARLY - let it initialize in parallel with Discord login.
1071
1079
  // This is the biggest startup bottleneck (can take 1-30 seconds to spawn and wait for ready)
1072
1080
  const currentDir = process.cwd();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@brianli/kimaki",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.4.73-brianli.2",
5
+ "version": "0.4.73-brianli.3",
6
6
  "repository": "https://github.com/remorses/kimaki",
7
7
  "bin": "bin.js",
8
8
  "files": [
@@ -22,8 +22,8 @@
22
22
  "prisma": "7.3.0",
23
23
  "tsx": "^4.20.5",
24
24
  "discord-digital-twin": "^0.0.1",
25
- "db": "^0.0.0",
26
25
  "opencode-cached-provider": "^0.0.1",
26
+ "db": "^0.0.0",
27
27
  "opencode-deterministic-provider": "^0.0.1"
28
28
  },
29
29
  "dependencies": {
@@ -57,8 +57,8 @@
57
57
  "xdg-basedir": "^5.1.0",
58
58
  "zod": "^4.3.6",
59
59
  "zustand": "^5.0.11",
60
- "errore": "^0.14.0",
61
- "traforo": "^0.0.9"
60
+ "traforo": "^0.0.9",
61
+ "errore": "^0.14.0"
62
62
  },
63
63
  "optionalDependencies": {
64
64
  "@discordjs/opus": "^0.10.0",
package/src/cli.ts CHANGED
@@ -1316,91 +1316,94 @@ async function run({
1316
1316
  const clientId = KIMAKI_CLIENT_ID || crypto.randomUUID()
1317
1317
  const clientSecret = KIMAKI_CLIENT_SECRET
1318
1318
  || crypto.randomBytes(32).toString('hex')
1319
- if (KIMAKI_CLIENT_ID && KIMAKI_CLIENT_SECRET) {
1319
+ const hasGatewayEnvCredentials = hasGatewayClientId && hasGatewayClientSecret
1320
+ if (hasGatewayEnvCredentials) {
1320
1321
  cliLogger.log(
1321
- 'Using KIMAKI_CLIENT_ID and KIMAKI_CLIENT_SECRET from environment for gateway mode.',
1322
+ 'Using KIMAKI_CLIENT_ID and KIMAKI_CLIENT_SECRET from environment for gateway mode. Skipping OAuth onboarding.',
1322
1323
  )
1323
1324
  }
1324
1325
 
1325
- const statePayload = JSON.stringify({ clientId, clientSecret } satisfies GatewayOAuthState)
1326
- const oauthUrl = generateBotInstallUrl({
1327
- clientId: KIMAKI_SHARED_APP_ID,
1328
- state: statePayload,
1329
- redirectUri: `${KIMAKI_WEBSITE_URL}/api/auth/callback/discord`,
1330
- responseType: 'code',
1331
- })
1326
+ if (!hasGatewayEnvCredentials) {
1327
+ const statePayload = JSON.stringify({ clientId, clientSecret } satisfies GatewayOAuthState)
1328
+ const oauthUrl = generateBotInstallUrl({
1329
+ clientId: KIMAKI_SHARED_APP_ID,
1330
+ state: statePayload,
1331
+ redirectUri: `${KIMAKI_WEBSITE_URL}/api/auth/callback/discord`,
1332
+ responseType: 'code',
1333
+ })
1332
1334
 
1333
- note(
1334
- `Open this URL to install the Kimaki bot in your Discord server:\n\n${oauthUrl}\n\nDo not share this URL with anyone — it contains your credentials.\n\nIf you don't have a server, create one first (+ button in the Discord sidebar).`,
1335
- 'Install Bot',
1336
- )
1335
+ note(
1336
+ `Open this URL to install the Kimaki bot in your Discord server:\n\n${oauthUrl}\n\nDo not share this URL with anyone — it contains your credentials.\n\nIf you don't have a server, create one first (+ button in the Discord sidebar).`,
1337
+ 'Install Bot',
1338
+ )
1337
1339
 
1338
- // Open URL in default browser
1339
- const { exec } = await import('node:child_process')
1340
- const openCmd =
1341
- process.platform === 'darwin'
1342
- ? 'open'
1343
- : process.platform === 'win32'
1344
- ? 'start'
1345
- : 'xdg-open'
1346
- exec(`${openCmd} "${oauthUrl}"`)
1347
-
1348
- // Poll until the user installs the bot in a Discord server.
1349
- // 600 attempts x 2s = 20 minutes timeout.
1350
- const s = spinner()
1351
- s.start('Waiting for a Discord server with the bot installed...')
1352
-
1353
- const pollUrl = new URL('/api/onboarding/status', KIMAKI_WEBSITE_URL)
1354
- pollUrl.searchParams.set('client_id', clientId)
1355
- pollUrl.searchParams.set('secret', clientSecret)
1356
-
1357
- let guildId: string | undefined
1358
- for (let attempt = 0; attempt < 600; attempt++) {
1359
- await new Promise((resolve) => {
1360
- setTimeout(resolve, 2000)
1361
- })
1340
+ // Open URL in default browser
1341
+ const { exec } = await import('node:child_process')
1342
+ const openCmd =
1343
+ process.platform === 'darwin'
1344
+ ? 'open'
1345
+ : process.platform === 'win32'
1346
+ ? 'start'
1347
+ : 'xdg-open'
1348
+ exec(`${openCmd} "${oauthUrl}"`)
1349
+
1350
+ // Poll until the user installs the bot in a Discord server.
1351
+ // 600 attempts x 2s = 20 minutes timeout.
1352
+ const s = spinner()
1353
+ s.start('Waiting for a Discord server with the bot installed...')
1354
+
1355
+ const pollUrl = new URL('/api/onboarding/status', KIMAKI_WEBSITE_URL)
1356
+ pollUrl.searchParams.set('client_id', clientId)
1357
+ pollUrl.searchParams.set('secret', clientSecret)
1358
+
1359
+ let guildId: string | undefined
1360
+ for (let attempt = 0; attempt < 600; attempt++) {
1361
+ await new Promise((resolve) => {
1362
+ setTimeout(resolve, 2000)
1363
+ })
1362
1364
 
1363
- // Progressive hints for users who may be stuck
1364
- if (attempt === 15) {
1365
- // ~30s
1366
- s.message(
1367
- 'Still waiting... Select a server in the Discord authorization page and click "Authorize"',
1368
- )
1369
- } else if (attempt === 45) {
1370
- // ~90s
1371
- s.message(
1372
- `Still waiting... If you don't see any servers, create one first (+ button in Discord sidebar), then reopen the URL above`,
1373
- )
1374
- } else if (attempt === 150) {
1375
- // ~5min
1376
- s.message(
1377
- `Still waiting... Reopen the install URL if you closed it:\n${oauthUrl}`,
1378
- )
1379
- }
1365
+ // Progressive hints for users who may be stuck
1366
+ if (attempt === 15) {
1367
+ // ~30s
1368
+ s.message(
1369
+ 'Still waiting... Select a server in the Discord authorization page and click "Authorize"',
1370
+ )
1371
+ } else if (attempt === 45) {
1372
+ // ~90s
1373
+ s.message(
1374
+ `Still waiting... If you don't see any servers, create one first (+ button in Discord sidebar), then reopen the URL above`,
1375
+ )
1376
+ } else if (attempt === 150) {
1377
+ // ~5min
1378
+ s.message(
1379
+ `Still waiting... Reopen the install URL if you closed it:\n${oauthUrl}`,
1380
+ )
1381
+ }
1380
1382
 
1381
- try {
1382
- const resp = await fetch(pollUrl.toString())
1383
- if (resp.ok) {
1384
- const data = (await resp.json()) as { guild_id?: string }
1385
- if (data.guild_id) {
1386
- guildId = data.guild_id
1387
- break
1383
+ try {
1384
+ const resp = await fetch(pollUrl.toString())
1385
+ if (resp.ok) {
1386
+ const data = (await resp.json()) as { guild_id?: string }
1387
+ if (data.guild_id) {
1388
+ guildId = data.guild_id
1389
+ break
1390
+ }
1388
1391
  }
1392
+ } catch {
1393
+ // Network error, retry
1389
1394
  }
1390
- } catch {
1391
- // Network error, retry
1392
1395
  }
1393
- }
1394
1396
 
1395
- if (!guildId) {
1396
- s.stop('Authorization timed out')
1397
- cliLogger.error(
1398
- 'Bot authorization timed out after 20 minutes. Please try again.',
1399
- )
1400
- process.exit(EXIT_NO_RESTART)
1401
- }
1397
+ if (!guildId) {
1398
+ s.stop('Authorization timed out')
1399
+ cliLogger.error(
1400
+ 'Bot authorization timed out after 20 minutes. Please try again.',
1401
+ )
1402
+ process.exit(EXIT_NO_RESTART)
1403
+ }
1402
1404
 
1403
- s.stop('Bot authorized successfully!')
1405
+ s.stop('Bot authorized successfully!')
1406
+ }
1404
1407
 
1405
1408
  await setBotMode({
1406
1409
  appId: KIMAKI_SHARED_APP_ID,
@@ -1520,6 +1523,12 @@ async function run({
1520
1523
  const shouldAddChannels =
1521
1524
  !isQuickStart || forceRestartOnboarding || Boolean(addChannels)
1522
1525
 
1526
+ store.setState({
1527
+ discordBaseUrl: isGatewayMode
1528
+ ? KIMAKI_GATEWAY_PROXY_REST_BASE_URL
1529
+ : 'https://discord.com',
1530
+ })
1531
+
1523
1532
  // Start OpenCode server EARLY - let it initialize in parallel with Discord login.
1524
1533
  // This is the biggest startup bottleneck (can take 1-30 seconds to spawn and wait for ready)
1525
1534
  const currentDir = process.cwd()