Merge branch 'main' into setup-lazy-env-reuse
This commit is contained in:
@@ -28,11 +28,11 @@ import k from 'kleur';
|
||||
|
||||
import * as setupLog from '../logs.js';
|
||||
import { brightSelect } from '../lib/bright-select.js';
|
||||
import { confirmThenOpen } from '../lib/browser.js';
|
||||
import { confirmThenOpen, formatNoteLink } from '../lib/browser.js';
|
||||
import { askOperatorRole } from '../lib/role-prompt.js';
|
||||
import { ensureAnswer, fail, runQuietChild } from '../lib/runner.js';
|
||||
import { accentGreen, brandBody, note } from '../lib/theme.js';
|
||||
import { readEnvKey } from '../environment.js';
|
||||
import { accentGreen, brandBody, fmtDuration, note } from '../lib/theme.js';
|
||||
|
||||
const DEFAULT_AGENT_NAME = 'Nano';
|
||||
const DISCORD_API = 'https://discord.com/api/v10';
|
||||
@@ -165,9 +165,8 @@ async function walkThroughBotCreation(): Promise<void> {
|
||||
' 2. In the "Bot" tab, click "Reset Token" and copy the token',
|
||||
' 3. On the same tab, enable "Message Content Intent"',
|
||||
' (under Privileged Gateway Intents)',
|
||||
'',
|
||||
k.dim(url),
|
||||
].join('\n'),
|
||||
formatNoteLink(url),
|
||||
].filter((line): line is string => line !== null).join('\n'),
|
||||
'Create a Discord bot',
|
||||
);
|
||||
await confirmThenOpen(url, 'Press Enter to open the Developer Portal');
|
||||
@@ -225,9 +224,8 @@ async function walkThroughServerCreation(): Promise<void> {
|
||||
' 1. In Discord, click the "+" at the bottom of the server list',
|
||||
' 2. Choose "Create My Own" → "For me and my friends"',
|
||||
' 3. Give it any name (e.g. "NanoClaw")',
|
||||
'',
|
||||
k.dim(url),
|
||||
].join('\n'),
|
||||
formatNoteLink(url),
|
||||
].filter((line): line is string => line !== null).join('\n'),
|
||||
'Create a Discord server',
|
||||
);
|
||||
await confirmThenOpen(url, 'Press Enter to open Discord');
|
||||
@@ -290,9 +288,8 @@ async function validateDiscordToken(token: string): Promise<string> {
|
||||
username?: string;
|
||||
message?: string;
|
||||
};
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (res.ok && data.username) {
|
||||
s.stop(`Found your bot: @${data.username}. ${k.dim(`(${elapsedS}s)`)}`);
|
||||
s.stop(`Found your bot: @${data.username}. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('discord-validate', 'success', Date.now() - start, {
|
||||
BOT_USERNAME: data.username,
|
||||
BOT_ID: data.id ?? '',
|
||||
@@ -310,8 +307,7 @@ async function validateDiscordToken(token: string): Promise<string> {
|
||||
'Copy the token again from the Developer Portal and retry setup.',
|
||||
);
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('discord-validate', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
@@ -339,7 +335,6 @@ async function fetchApplicationInfo(token: string): Promise<AppInfo> {
|
||||
team?: unknown;
|
||||
message?: string;
|
||||
};
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (!res.ok || !data.id || !data.verify_key) {
|
||||
const reason = data.message ?? `HTTP ${res.status}`;
|
||||
s.stop(`Couldn't read application info: ${reason}`, 1);
|
||||
@@ -352,7 +347,7 @@ async function fetchApplicationInfo(token: string): Promise<AppInfo> {
|
||||
'Re-run setup. If it keeps failing, check the bot token has the right scopes.',
|
||||
);
|
||||
}
|
||||
s.stop(`Got your application details. ${k.dim(`(${elapsedS}s)`)}`);
|
||||
s.stop(`Got your application details. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
// owner is populated for solo applications; team-owned apps return a
|
||||
// team object instead and we'll fall back to a manual user-id prompt.
|
||||
const owner =
|
||||
@@ -370,8 +365,7 @@ async function fetchApplicationInfo(token: string): Promise<AppInfo> {
|
||||
owner,
|
||||
};
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('discord-app-info', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
@@ -451,9 +445,8 @@ async function promptInviteBot(
|
||||
'',
|
||||
' 1. Pick any server you\'re in (a personal one is fine)',
|
||||
' 2. Click "Authorize"',
|
||||
'',
|
||||
k.dim(url),
|
||||
].join('\n'),
|
||||
formatNoteLink(url),
|
||||
].filter((line): line is string => line !== null).join('\n'),
|
||||
'Add bot to a server',
|
||||
);
|
||||
await confirmThenOpen(url, 'Press Enter to open the invite page');
|
||||
@@ -480,7 +473,6 @@ async function openDmChannel(token: string, userId: string): Promise<string> {
|
||||
body: JSON.stringify({ recipient_id: userId }),
|
||||
});
|
||||
const data = (await res.json()) as { id?: string; message?: string };
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (!res.ok || !data.id) {
|
||||
const reason = data.message ?? `HTTP ${res.status}`;
|
||||
s.stop(`Couldn't open a DM channel: ${reason}`, 1);
|
||||
@@ -493,14 +485,13 @@ async function openDmChannel(token: string, userId: string): Promise<string> {
|
||||
'Make sure the bot is in a server you\'re also in, then retry setup.',
|
||||
);
|
||||
}
|
||||
s.stop(`DM channel ready. ${k.dim(`(${elapsedS}s)`)}`);
|
||||
s.stop(`DM channel ready. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('discord-open-dm', 'success', Date.now() - start, {
|
||||
DM_CHANNEL_ID: data.id,
|
||||
});
|
||||
return data.id;
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Discord. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('discord-open-dm', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
writeStepEntry,
|
||||
} from '../lib/runner.js';
|
||||
import { askOperatorRole } from '../lib/role-prompt.js';
|
||||
import { accentGreen, note } from '../lib/theme.js';
|
||||
import { accentGreen, fmtDuration, note } from '../lib/theme.js';
|
||||
|
||||
const DEFAULT_AGENT_NAME = 'Nano';
|
||||
|
||||
@@ -324,8 +324,7 @@ async function restartService(): Promise<void> {
|
||||
// Give the adapter a moment to connect to signal-cli before
|
||||
// init-first-agent's welcome DM hits the delivery path.
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
const elapsed = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`NanoClaw restarted. ${k.dim(`(${elapsed}s)`)}`);
|
||||
s.stop(`NanoClaw restarted. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('signal-restart', 'success', Date.now() - start, {
|
||||
PLATFORM: platform,
|
||||
});
|
||||
|
||||
@@ -25,11 +25,11 @@ import * as p from '@clack/prompts';
|
||||
import k from 'kleur';
|
||||
|
||||
import * as setupLog from '../logs.js';
|
||||
import { confirmThenOpen } from '../lib/browser.js';
|
||||
import { confirmThenOpen, formatNoteLink } from '../lib/browser.js';
|
||||
import { askOperatorRole } from '../lib/role-prompt.js';
|
||||
import { ensureAnswer, fail, runQuietChild } from '../lib/runner.js';
|
||||
import { accentGreen, note, wrapForGutter } from '../lib/theme.js';
|
||||
import { readEnvKey } from '../environment.js';
|
||||
import { accentGreen, fmtDuration, note, wrapForGutter } from '../lib/theme.js';
|
||||
|
||||
const SLACK_API = 'https://slack.com/api';
|
||||
const SLACK_APPS_URL = 'https://api.slack.com/apps';
|
||||
@@ -136,9 +136,8 @@ async function walkThroughAppCreation(): Promise<void> {
|
||||
' slash commands and messages from the messages tab"',
|
||||
' 4. Basic Information → copy the "Signing Secret"',
|
||||
' 5. Install to Workspace → copy the "Bot User OAuth Token" (xoxb-…)',
|
||||
'',
|
||||
k.dim(SLACK_APPS_URL),
|
||||
].join('\n'),
|
||||
formatNoteLink(SLACK_APPS_URL),
|
||||
].filter((line): line is string => line !== null).join('\n'),
|
||||
'Create a Slack app',
|
||||
);
|
||||
await confirmThenOpen(SLACK_APPS_URL, 'Press Enter to open Slack app settings');
|
||||
@@ -242,10 +241,9 @@ async function validateSlackToken(token: string): Promise<WorkspaceInfo> {
|
||||
user_id?: string;
|
||||
error?: string;
|
||||
};
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (data.ok && data.team && data.user) {
|
||||
s.stop(
|
||||
`Connected to ${data.team} as @${data.user}. ${k.dim(`(${elapsedS}s)`)}`,
|
||||
`Connected to ${data.team} as @${data.user}. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`,
|
||||
);
|
||||
const info: WorkspaceInfo = {
|
||||
teamName: data.team,
|
||||
@@ -274,8 +272,7 @@ async function validateSlackToken(token: string): Promise<WorkspaceInfo> {
|
||||
: `Slack said "${reason}". Check the token scopes and workspace install, then retry.`,
|
||||
);
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Slack. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Slack. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('slack-validate', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
@@ -335,9 +332,8 @@ async function openDmChannel(token: string, userId: string): Promise<string> {
|
||||
channel?: { id?: string };
|
||||
error?: string;
|
||||
};
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (data.ok && data.channel?.id) {
|
||||
s.stop(`DM channel ready. ${k.dim(`(${elapsedS}s)`)}`);
|
||||
s.stop(`DM channel ready. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('slack-open-dm', 'success', Date.now() - start, {
|
||||
DM_CHANNEL_ID: data.channel.id,
|
||||
});
|
||||
@@ -361,8 +357,7 @@ async function openDmChannel(token: string, userId: string): Promise<string> {
|
||||
`Slack said "${reason}". Check the member ID and app permissions, then retry.`,
|
||||
);
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Slack. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Slack. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('slack-open-dm', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
|
||||
@@ -21,7 +21,7 @@ import * as p from '@clack/prompts';
|
||||
import k from 'kleur';
|
||||
|
||||
import * as setupLog from '../logs.js';
|
||||
import { confirmThenOpen } from '../lib/browser.js';
|
||||
import { confirmThenOpen, formatNoteLink } from '../lib/browser.js';
|
||||
import { askOperatorRole } from '../lib/role-prompt.js';
|
||||
import {
|
||||
type Block,
|
||||
@@ -33,8 +33,8 @@ import {
|
||||
spawnStep,
|
||||
writeStepEntry,
|
||||
} from '../lib/runner.js';
|
||||
import { accentGreen, brandBold, fitToWidth, note } from '../lib/theme.js';
|
||||
import { readEnvKey } from '../environment.js';
|
||||
import { accentGreen, brandBold, fitToWidth, fmtDuration, note } from '../lib/theme.js';
|
||||
|
||||
const DEFAULT_AGENT_NAME = 'Nano';
|
||||
|
||||
@@ -51,9 +51,8 @@ export async function runTelegramChannel(displayName: string): Promise<void> {
|
||||
note(
|
||||
[
|
||||
`Opening @${botUsername} in Telegram so it's ready when the pairing code shows up.`,
|
||||
'',
|
||||
k.dim(botUrl),
|
||||
].join('\n'),
|
||||
formatNoteLink(botUrl),
|
||||
].filter((line): line is string => line !== null).join('\n'),
|
||||
'Open Telegram',
|
||||
);
|
||||
await confirmThenOpen(botUrl, 'Press Enter to open Telegram');
|
||||
@@ -192,10 +191,9 @@ async function validateTelegramToken(token: string): Promise<string> {
|
||||
result?: { username?: string; id?: number };
|
||||
description?: string;
|
||||
};
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
if (data.ok && data.result?.username) {
|
||||
const username = data.result.username;
|
||||
s.stop(`Found your bot: @${username}. ${k.dim(`(${elapsedS}s)`)}`);
|
||||
s.stop(`Found your bot: @${username}. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('telegram-validate', 'success', Date.now() - start, {
|
||||
BOT_USERNAME: username,
|
||||
BOT_ID: data.result.id ?? '',
|
||||
@@ -213,8 +211,7 @@ async function validateTelegramToken(token: string): Promise<string> {
|
||||
'Copy the token again from @BotFather and try setup once more.',
|
||||
);
|
||||
} catch (err) {
|
||||
const elapsedS = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`Couldn't reach Telegram. ${k.dim(`(${elapsedS}s)`)}`, 1);
|
||||
s.stop(`Couldn't reach Telegram. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`, 1);
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setupLog.step('telegram-validate', 'failed', Date.now() - start, {
|
||||
ERROR: message,
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
writeStepEntry,
|
||||
} from '../lib/runner.js';
|
||||
import { askOperatorRole } from '../lib/role-prompt.js';
|
||||
import { accentGreen, brandBody, brandBold, note } from '../lib/theme.js';
|
||||
import { accentGreen, brandBody, brandBold, fmtDuration, note } from '../lib/theme.js';
|
||||
|
||||
const DEFAULT_AGENT_NAME = 'Nano';
|
||||
const AUTH_CREDS_PATH = path.join(process.cwd(), 'store', 'auth', 'creds.json');
|
||||
@@ -379,8 +379,7 @@ async function restartService(): Promise<void> {
|
||||
// Give the adapter a moment to reconnect before init-first-agent's
|
||||
// welcome DM hits the delivery path.
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
const elapsed = Math.round((Date.now() - start) / 1000);
|
||||
s.stop(`NanoClaw restarted. ${k.dim(`(${elapsed}s)`)}`);
|
||||
s.stop(`NanoClaw restarted. ${k.dim(`(${fmtDuration(Date.now() - start)})`)}`);
|
||||
setupLog.step('whatsapp-restart', 'success', Date.now() - start, {
|
||||
PLATFORM: platform,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user