Merge branch 'main' into chore/label-pr-fork-support
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nanoclaw",
|
"name": "nanoclaw",
|
||||||
"version": "2.0.10",
|
"version": "2.0.11",
|
||||||
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
|
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.33.0",
|
"packageManager": "pnpm@10.33.0",
|
||||||
|
|||||||
55
setup/verify.test.ts
Normal file
55
setup/verify.test.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { determineVerifyStatus } from './verify.js';
|
||||||
|
|
||||||
|
const healthyBase = {
|
||||||
|
service: 'running' as const,
|
||||||
|
credentials: 'configured',
|
||||||
|
anyChannelConfigured: false,
|
||||||
|
registeredGroups: 1,
|
||||||
|
agentPing: 'ok' as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('determineVerifyStatus', () => {
|
||||||
|
it('accepts a working CLI-only install', () => {
|
||||||
|
expect(determineVerifyStatus(healthyBase)).toBe('success');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a messaging-channel install when CLI ping is skipped', () => {
|
||||||
|
expect(
|
||||||
|
determineVerifyStatus({
|
||||||
|
...healthyBase,
|
||||||
|
anyChannelConfigured: true,
|
||||||
|
agentPing: 'skipped',
|
||||||
|
}),
|
||||||
|
).toBe('success');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails when neither CLI nor messaging channels are usable', () => {
|
||||||
|
expect(
|
||||||
|
determineVerifyStatus({
|
||||||
|
...healthyBase,
|
||||||
|
agentPing: 'skipped',
|
||||||
|
}),
|
||||||
|
).toBe('failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails when the CLI agent does not respond', () => {
|
||||||
|
expect(
|
||||||
|
determineVerifyStatus({
|
||||||
|
...healthyBase,
|
||||||
|
anyChannelConfigured: true,
|
||||||
|
agentPing: 'no_reply',
|
||||||
|
}),
|
||||||
|
).toBe('failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails when no agent groups are registered', () => {
|
||||||
|
expect(
|
||||||
|
determineVerifyStatus({
|
||||||
|
...healthyBase,
|
||||||
|
registeredGroups: 0,
|
||||||
|
}),
|
||||||
|
).toBe('failed');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -14,7 +14,7 @@ import Database from 'better-sqlite3';
|
|||||||
import { DATA_DIR } from '../src/config.js';
|
import { DATA_DIR } from '../src/config.js';
|
||||||
import { readEnvFile } from '../src/env.js';
|
import { readEnvFile } from '../src/env.js';
|
||||||
import { log } from '../src/log.js';
|
import { log } from '../src/log.js';
|
||||||
import { pingCliAgent } from './lib/agent-ping.js';
|
import { pingCliAgent, type PingResult } from './lib/agent-ping.js';
|
||||||
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
|
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
|
||||||
import {
|
import {
|
||||||
getPlatform,
|
getPlatform,
|
||||||
@@ -227,15 +227,15 @@ export async function run(_args: string[]): Promise<void> {
|
|||||||
log.info('Agent ping result', { agentPing });
|
log.info('Agent ping result', { agentPing });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine overall status
|
// Determine overall status. A CLI-only install is valid when the local
|
||||||
const status =
|
// agent round-trip succeeds; messaging app credentials are optional.
|
||||||
service === 'running' &&
|
const status = determineVerifyStatus({
|
||||||
credentials !== 'missing' &&
|
service,
|
||||||
anyChannelConfigured &&
|
credentials,
|
||||||
registeredGroups > 0 &&
|
anyChannelConfigured,
|
||||||
(agentPing === 'ok' || agentPing === 'skipped')
|
registeredGroups,
|
||||||
? 'success'
|
agentPing,
|
||||||
: 'failed';
|
});
|
||||||
|
|
||||||
log.info('Verification complete', { status, channelAuth });
|
log.info('Verification complete', { status, channelAuth });
|
||||||
|
|
||||||
@@ -255,6 +255,25 @@ export async function run(_args: string[]): Promise<void> {
|
|||||||
if (status === 'failed') process.exit(1);
|
if (status === 'failed') process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function determineVerifyStatus(input: {
|
||||||
|
service: 'not_found' | 'stopped' | 'running' | 'running_other_checkout';
|
||||||
|
credentials: string;
|
||||||
|
anyChannelConfigured: boolean;
|
||||||
|
registeredGroups: number;
|
||||||
|
agentPing: PingResult | 'skipped';
|
||||||
|
}): 'success' | 'failed' {
|
||||||
|
const cliAgentResponds = input.agentPing === 'ok';
|
||||||
|
const hasUsableChannel = input.anyChannelConfigured || cliAgentResponds;
|
||||||
|
|
||||||
|
return input.service === 'running' &&
|
||||||
|
input.credentials !== 'missing' &&
|
||||||
|
hasUsableChannel &&
|
||||||
|
input.registeredGroups > 0 &&
|
||||||
|
(cliAgentResponds || input.agentPing === 'skipped')
|
||||||
|
? 'success'
|
||||||
|
: 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a PID, resolve the script path the process is executing (i.e. the
|
* Given a PID, resolve the script path the process is executing (i.e. the
|
||||||
* first `.js` / `.ts` / `.mjs` arg after `node`). Returns null on any
|
* first `.js` / `.ts` / `.mjs` arg after `node`). Returns null on any
|
||||||
|
|||||||
@@ -125,7 +125,11 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
|||||||
let setupConfig: ChannelSetup;
|
let setupConfig: ChannelSetup;
|
||||||
let gatewayAbort: AbortController | null = null;
|
let gatewayAbort: AbortController | null = null;
|
||||||
|
|
||||||
async function messageToInbound(message: ChatMessage, isMention: boolean, isGroup?: boolean): Promise<InboundMessage> {
|
async function messageToInbound(
|
||||||
|
message: ChatMessage,
|
||||||
|
isMention: boolean,
|
||||||
|
isGroup?: boolean,
|
||||||
|
): Promise<InboundMessage> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const serialized = message.toJSON() as Record<string, any>;
|
const serialized = message.toJSON() as Record<string, any>;
|
||||||
|
|
||||||
@@ -216,7 +220,11 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
|||||||
// wirings still fire on in-thread mentions.
|
// wirings still fire on in-thread mentions.
|
||||||
chat.onSubscribedMessage(async (thread, message) => {
|
chat.onSubscribedMessage(async (thread, message) => {
|
||||||
const channelId = adapter.channelIdFromThreadId(thread.id);
|
const channelId = adapter.channelIdFromThreadId(thread.id);
|
||||||
await setupConfig.onInbound(channelId, thread.id, await messageToInbound(message, message.isMention === true, true));
|
await setupConfig.onInbound(
|
||||||
|
channelId,
|
||||||
|
thread.id,
|
||||||
|
await messageToInbound(message, message.isMention === true, true),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// @mention in an unsubscribed thread — SDK-confirmed bot mention.
|
// @mention in an unsubscribed thread — SDK-confirmed bot mention.
|
||||||
|
|||||||
Reference in New Issue
Block a user