Compare commits
12 Commits
v0.1.1-tes
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
9fe8ab94e8 | |
|
|
f4fe52aff1 | |
|
|
b29037526b | |
|
|
ece7db4cf2 | |
|
|
2f4f8d4030 | |
|
|
47d3711f43 | |
|
|
2beb7e1051 | |
|
|
dfbd601307 | |
|
|
f7d6cb125c | |
|
|
3bf26335a6 | |
|
|
06c5c302e9 | |
|
|
e7d9e972d4 |
|
|
@ -1,6 +1,4 @@
|
||||||
# Local runtime config for client-finder skill
|
# Local runtime config for client-finder skill
|
||||||
# Copy to .env.local in the same folder and fill real values.
|
# Auth is handled automatically via ~/.openclaw/.env
|
||||||
|
|
||||||
AUTH_BASE=https://api-gw-test.yuanwei-lnc.com
|
AUTH_BASE=https://api-gw-test.yuanwei-lnc.com
|
||||||
CLIENT_KEY=sk_xxx_replace_with_real_key
|
|
||||||
|
|
||||||
|
|
|
||||||
5
SKILL.md
5
SKILL.md
|
|
@ -8,7 +8,7 @@ description: "找客户、找买家、开发客户、cold outreach。当用户
|
||||||
Skill-credit + ecom run-flow: query expansion → `/ecom/cold-outreach/run-flow`.
|
Skill-credit + ecom run-flow: query expansion → `/ecom/cold-outreach/run-flow`.
|
||||||
Execution mode is fire-and-return: start workflow fast and return accepted immediately; terminal callbacks are handled by backend webhook delivery.
|
Execution mode is fire-and-return: start workflow fast and return accepted immediately; terminal callbacks are handled by backend webhook delivery.
|
||||||
|
|
||||||
> Auth (CLIENT_KEY) is loaded automatically from `~/.openclaw/.env` by auth-runtime. No need to pass it.
|
> Auth is handled automatically by auth-runtime via `~/.openclaw/.env`.
|
||||||
|
|
||||||
## Run Skill
|
## Run Skill
|
||||||
|
|
||||||
|
|
@ -51,8 +51,7 @@ For client onboarding and billing flow (Chinese), read [how-to-use.md](how-to-us
|
||||||
- Remove leading `cold-outreach:` prefix (case-insensitive).
|
- Remove leading `cold-outreach:` prefix (case-insensitive).
|
||||||
|
|
||||||
2. Exchange runtime token.
|
2. Exchange runtime token.
|
||||||
- auth-runtime handles this automatically via `~/.openclaw/.env`.
|
- Auth handled automatically by auth-runtime.
|
||||||
- Calls `POST /auth/skill-credit/session` with `CLIENT_KEY`, caches token with TTL.
|
|
||||||
|
|
||||||
3. Expand query.
|
3. Expand query.
|
||||||
- Build candidate queries from skill logic (`rule`) or `QUERY_EXPANSION_JSON` (`llm`).
|
- Build candidate queries from skill logic (`rule`) or `QUERY_EXPANSION_JSON` (`llm`).
|
||||||
|
|
|
||||||
6
bun.lock
6
bun.lock
|
|
@ -5,7 +5,7 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "client-finder",
|
"name": "client-finder",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clawd/auth-runtime": "file:../_shared/auth-runtime",
|
"@clawd/auth-runtime": "file:../auth-runtime",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@clawd/auth-runtime": ["@clawd/auth-runtime@file:../_shared/auth-runtime", { "devDependencies": { "@types/node": "^25.3.3", "typescript": "^5.9.3" } }],
|
"@clawd/auth-runtime": ["@clawd/auth-runtime@file:../auth-runtime", { "devDependencies": { "@types/node": "^25.3.3", "typescript": "^5.9.3" } }],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
|
"bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# Auto-install auth-rt if not found
|
||||||
|
if ! command -v auth-rt &>/dev/null && [ ! -x "$HOME/.local/bin/auth-rt" ]; then
|
||||||
|
echo "auth-rt not found, installing..."
|
||||||
|
_FORGEJO="http://192.168.0.108:3030"
|
||||||
|
_OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||||
|
_ARCH="$(uname -m)"; case "$_ARCH" in x86_64) _ARCH="amd64";; aarch64) _ARCH="arm64";; esac
|
||||||
|
_URL="$_FORGEJO/agent-skills/auth-runtime/releases/download/latest/auth-rt-${_OS}-${_ARCH}"
|
||||||
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
if curl -fsSL "$_URL" -o "$HOME/.local/bin/auth-rt" 2>/dev/null; then
|
||||||
|
chmod +x "$HOME/.local/bin/auth-rt"
|
||||||
|
echo "auth-rt installed (downloaded)"
|
||||||
|
else
|
||||||
|
echo "Download failed, building from source..."
|
||||||
|
_SRC="$HOME/.local/share/auth-runtime"
|
||||||
|
if [ -d "$_SRC/.git" ]; then git -C "$_SRC" pull --ff-only
|
||||||
|
else git clone --depth 1 "$_FORGEJO/agent-skills/auth-runtime.git" "$_SRC"
|
||||||
|
fi
|
||||||
|
bash "$_SRC/install.sh"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
@ -18,7 +18,5 @@
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {}
|
||||||
"@clawd/auth-runtime": "git+http://192.168.0.108:3030/agent-skills/auth-runtime.git"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ async function main(): Promise<void> {
|
||||||
if (parsed.clientKey) process.env.CLIENT_KEY = parsed.clientKey;
|
if (parsed.clientKey) process.env.CLIENT_KEY = parsed.clientKey;
|
||||||
if (parsed.authBase) process.env.AUTH_BASE = parsed.authBase;
|
if (parsed.authBase) process.env.AUTH_BASE = parsed.authBase;
|
||||||
|
|
||||||
const result = await runClientFinder(parsed.query, parsed.country, parsed.dryRun);
|
const result = await runClientFinder(parsed.query, parsed.country || 'US', parsed.dryRun);
|
||||||
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.log(JSON.stringify(result, null, 2));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* Thin CLI wrapper for auth-runtime.
|
||||||
|
*
|
||||||
|
* Copy this file into your skill's src/ directory. It calls the
|
||||||
|
* `auth-rt` binary (a standalone Go executable), so the skill has
|
||||||
|
* zero npm/runtime dependency on auth-runtime.
|
||||||
|
*
|
||||||
|
* Prerequisites:
|
||||||
|
* `auth-rt` must be in PATH or at ~/.local/bin/auth-rt
|
||||||
|
* (install.sh handles this automatically)
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* import { createSkillClient } from './auth-cli.ts';
|
||||||
|
* const client = createSkillClient();
|
||||||
|
* const res = await client.post('/ecom/tasks/scrape', { url: '...' });
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as os from 'os';
|
||||||
|
|
||||||
|
const home = process.env.HOME || os.homedir();
|
||||||
|
const AUTH_RT_BIN = process.env.AUTH_RT_BIN
|
||||||
|
|| (() => {
|
||||||
|
// Check if auth-rt is in PATH
|
||||||
|
const which = spawnSync('which', ['auth-rt'], { encoding: 'utf-8' });
|
||||||
|
if (which.status === 0 && which.stdout.trim()) {
|
||||||
|
return which.stdout.trim();
|
||||||
|
}
|
||||||
|
return path.join(home, '.local', 'bin', 'auth-rt');
|
||||||
|
})();
|
||||||
|
|
||||||
|
export interface ApiResponse {
|
||||||
|
status: number;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SessionResponse {
|
||||||
|
accessToken: string;
|
||||||
|
expiresIn: number;
|
||||||
|
ownerSessionToken?: string;
|
||||||
|
hookUrl?: string;
|
||||||
|
hookToken?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkillClientOptions {
|
||||||
|
apiBase?: string;
|
||||||
|
dryRun?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCli(...args: string[]): string {
|
||||||
|
const result = spawnSync(AUTH_RT_BIN, args, {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`auth-rt spawn failed: ${result.error.message}`);
|
||||||
|
}
|
||||||
|
if (result.status !== 0) {
|
||||||
|
throw new Error(`auth-rt failed (exit ${result.status}): ${(result.stderr || '').trim()}`);
|
||||||
|
}
|
||||||
|
return (result.stdout || '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SkillClient {
|
||||||
|
private readonly apiBase?: string;
|
||||||
|
private readonly dryRun: boolean;
|
||||||
|
|
||||||
|
constructor(options: SkillClientOptions = {}) {
|
||||||
|
this.apiBase = options.apiBase;
|
||||||
|
this.dryRun = options.dryRun ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async session(): Promise<SessionResponse> {
|
||||||
|
if (this.dryRun) {
|
||||||
|
return { accessToken: '<dry-run-token>', expiresIn: 900 };
|
||||||
|
}
|
||||||
|
return JSON.parse(runCli('session'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(urlPath: string): Promise<ApiResponse> {
|
||||||
|
return this.request('GET', urlPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async post(urlPath: string, body?: unknown): Promise<ApiResponse> {
|
||||||
|
return this.request('POST', urlPath, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async put(urlPath: string, body?: unknown): Promise<ApiResponse> {
|
||||||
|
return this.request('PUT', urlPath, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async patch(urlPath: string, body?: unknown): Promise<ApiResponse> {
|
||||||
|
return this.request('PATCH', urlPath, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(urlPath: string, body?: unknown): Promise<ApiResponse> {
|
||||||
|
return this.request('DELETE', urlPath, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async request(method: string, urlPath: string, body?: unknown): Promise<ApiResponse> {
|
||||||
|
if (this.dryRun) {
|
||||||
|
return { status: 200, body: JSON.stringify({ dryRun: true, method, path: urlPath }) };
|
||||||
|
}
|
||||||
|
const args = ['request', method, urlPath];
|
||||||
|
if (body != null) {
|
||||||
|
args.push('--body', JSON.stringify(body));
|
||||||
|
}
|
||||||
|
if (this.apiBase) {
|
||||||
|
args.push('--api-base', this.apiBase);
|
||||||
|
}
|
||||||
|
return JSON.parse(runCli(...args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSkillClient(options?: SkillClientOptions): SkillClient {
|
||||||
|
return new SkillClient(options);
|
||||||
|
}
|
||||||
244
src/index.ts
244
src/index.ts
|
|
@ -1,199 +1,80 @@
|
||||||
import type { EnvConfig, OutputResult } from './types.js';
|
import type { OutputResult } from './types.js';
|
||||||
import { createEnvConfig as createBaseEnvConfig, getAccessToken } from '@clawd/auth-runtime';
|
import { createSkillClient } from './auth-cli.ts';
|
||||||
import { normalizeQuery, resolveExpansion } from './expansion.js';
|
import { normalizeQuery, resolveExpansion } from './expansion.js';
|
||||||
import { startWorkflow } from './workflow.js';
|
import { startWorkflow } from './workflow.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create client-finder specific environment configuration
|
|
||||||
* Extends the shared auth config with skill-specific fields
|
|
||||||
*/
|
|
||||||
function createEnvConfig(): EnvConfig {
|
|
||||||
const baseConfig = createBaseEnvConfig();
|
|
||||||
return {
|
|
||||||
...baseConfig,
|
|
||||||
queryExpansionJson: process.env.QUERY_EXPANSION_JSON || '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main entry point for client-finder skill
|
|
||||||
*/
|
|
||||||
export async function runClientFinder(
|
export async function runClientFinder(
|
||||||
query: string,
|
query: string,
|
||||||
country: string,
|
country: string = 'US',
|
||||||
dryRun: boolean = false,
|
dryRun: boolean = false,
|
||||||
): Promise<OutputResult> {
|
): Promise<OutputResult> {
|
||||||
const config = createEnvConfig();
|
|
||||||
|
|
||||||
// Validate query
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return createFailedResult('', 'missing query argument');
|
return result('failed', { error: 'missing query argument' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize query
|
|
||||||
const rawQuery = normalizeQuery(query);
|
const rawQuery = normalizeQuery(query);
|
||||||
const countryUpper = country.toUpperCase();
|
const countryUpper = country.toUpperCase();
|
||||||
const countryLower = country.toLowerCase();
|
const llmExpansion = process.env.QUERY_EXPANSION_JSON || '';
|
||||||
|
const expansion = resolveExpansion(rawQuery, countryUpper, llmExpansion);
|
||||||
|
|
||||||
// Resolve expansion
|
// Build expansion context
|
||||||
const expansion = resolveExpansion(
|
const ctx: Partial<OutputResult> = {
|
||||||
rawQuery,
|
inputQuery: rawQuery,
|
||||||
countryUpper,
|
expandedQueries: expansion.expandedQueries,
|
||||||
config.queryExpansionJson,
|
primaryQuery: expansion.primaryQuery,
|
||||||
);
|
expansionStatus: expansion.ok ? 'success' : 'failed',
|
||||||
|
expansionSource: expansion.expansionSource,
|
||||||
|
expansionError: expansion.error || null,
|
||||||
|
usedFallbackQuery: false,
|
||||||
|
};
|
||||||
|
|
||||||
let expandedQueries = expansion.expandedQueries;
|
// Handle expansion failure — fallback to raw query
|
||||||
let primaryQuery = expansion.primaryQuery;
|
|
||||||
let expansionStatus = expansion.ok ? 'success' : ('failed' as const);
|
|
||||||
let expansionSource = expansion.expansionSource;
|
|
||||||
let expansionError = expansion.error || '';
|
|
||||||
let usedFallbackQuery = false;
|
|
||||||
|
|
||||||
// Handle expansion failure
|
|
||||||
if (!expansion.ok) {
|
if (!expansion.ok) {
|
||||||
if (rawQuery) {
|
if (!rawQuery) {
|
||||||
primaryQuery = rawQuery;
|
return result('failed', { ...ctx, error: expansion.error || 'query is empty' });
|
||||||
expandedQueries = [rawQuery];
|
|
||||||
expansionSource = 'raw_query';
|
|
||||||
expansionError = `query expansion failed: ${expansion.error}; fallback to raw query`;
|
|
||||||
usedFallbackQuery = true;
|
|
||||||
} else {
|
|
||||||
expansionError = expansionError || 'query expansion failed: primary query is empty';
|
|
||||||
return createFailedResult(
|
|
||||||
rawQuery,
|
|
||||||
expansionError,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
ctx.primaryQuery = rawQuery;
|
||||||
|
ctx.expandedQueries = [rawQuery];
|
||||||
|
ctx.expansionSource = 'raw_query';
|
||||||
|
ctx.expansionError = `query expansion failed: ${expansion.error}; fallback to raw query`;
|
||||||
|
ctx.usedFallbackQuery = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate primary query
|
if (!ctx.primaryQuery) {
|
||||||
if (!primaryQuery) {
|
return result('failed', { ...ctx, error: 'primary query is empty after expansion' });
|
||||||
expansionError = expansionError || 'query expansion failed: primary query is empty';
|
|
||||||
return createFailedResult(
|
|
||||||
rawQuery,
|
|
||||||
expansionError,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dry run mode
|
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
return createSuccessResult(
|
return result('success', { ...ctx, workflowStatus: 'dry_run' });
|
||||||
rawQuery,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
'',
|
|
||||||
'dry_run',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate CLIENT_KEY in live mode
|
let client;
|
||||||
if (!config.clientKey) {
|
|
||||||
return createFailedResult(
|
|
||||||
rawQuery,
|
|
||||||
'missing required env: CLIENT_KEY',
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Exchange CLIENT_KEY for runtime access token
|
|
||||||
let accessToken = '';
|
|
||||||
try {
|
try {
|
||||||
accessToken = await getAccessToken(dryRun, config);
|
client = createSkillClient();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : 'failed to exchange skill session token';
|
return result('failed', { ...ctx, error: error instanceof Error ? error.message : String(error) });
|
||||||
return createFailedResult(
|
|
||||||
rawQuery,
|
|
||||||
errorMsg,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Start workflow with runtime access token
|
const wf = await startWorkflow(client, ctx.primaryQuery, country.toLowerCase());
|
||||||
const workflowResult = await startWorkflow(
|
|
||||||
config,
|
|
||||||
dryRun,
|
|
||||||
accessToken,
|
|
||||||
primaryQuery,
|
|
||||||
countryLower,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!workflowResult.workflowId) {
|
if (!wf.workflowId) {
|
||||||
return createFailedResult(
|
return result('failed', { ...ctx, error: `start failed: ${wf.error}` });
|
||||||
rawQuery,
|
|
||||||
`start failed: ${workflowResult.error}`,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Return accepted immediately
|
return result('success', { ...ctx, workflowId: wf.workflowId, workflowStatus: 'accepted' });
|
||||||
return createSuccessResult(
|
|
||||||
rawQuery,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
'',
|
|
||||||
'accepted',
|
|
||||||
workflowResult.workflowId,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function result(status: 'success' | 'failed', overrides: Partial<OutputResult> = {}): OutputResult {
|
||||||
* Create a failed result
|
|
||||||
*/
|
|
||||||
function createFailedResult(
|
|
||||||
inputQuery: string,
|
|
||||||
error: string,
|
|
||||||
expandedQueries: string[] = [],
|
|
||||||
primaryQuery: string = '',
|
|
||||||
expansionStatus: 'success' | 'failed' = 'failed',
|
|
||||||
expansionSource: 'llm' | 'rule' | 'raw_query' | '' = '',
|
|
||||||
expansionError: string | null = null,
|
|
||||||
usedFallbackQuery: boolean = false,
|
|
||||||
): OutputResult {
|
|
||||||
return {
|
return {
|
||||||
status: 'failed',
|
status,
|
||||||
error: error || null,
|
error: null,
|
||||||
inputQuery,
|
inputQuery: '',
|
||||||
expandedQueries,
|
expandedQueries: [],
|
||||||
primaryQuery,
|
primaryQuery: '',
|
||||||
expansionStatus,
|
expansionStatus: 'failed',
|
||||||
expansionSource,
|
expansionSource: '',
|
||||||
expansionError: expansionError || null,
|
expansionError: null,
|
||||||
usedFallbackQuery,
|
usedFallbackQuery: false,
|
||||||
runId: '',
|
runId: '',
|
||||||
workflowId: '',
|
workflowId: '',
|
||||||
workflowStatus: '',
|
workflowStatus: '',
|
||||||
|
|
@ -202,41 +83,6 @@ function createFailedResult(
|
||||||
businessesCount: 0,
|
businessesCount: 0,
|
||||||
contactsCount: 0,
|
contactsCount: 0,
|
||||||
uniqueContactDomains: 0,
|
uniqueContactDomains: 0,
|
||||||
};
|
...overrides,
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a success result
|
|
||||||
*/
|
|
||||||
function createSuccessResult(
|
|
||||||
inputQuery: string,
|
|
||||||
expandedQueries: string[],
|
|
||||||
primaryQuery: string,
|
|
||||||
expansionStatus: 'success' | 'failed',
|
|
||||||
expansionSource: 'llm' | 'rule' | 'raw_query' | '',
|
|
||||||
expansionError: string | null,
|
|
||||||
usedFallbackQuery: boolean,
|
|
||||||
runId: string,
|
|
||||||
workflowStatus: string,
|
|
||||||
workflowId: string = '',
|
|
||||||
): OutputResult {
|
|
||||||
return {
|
|
||||||
status: 'success',
|
|
||||||
error: null,
|
|
||||||
inputQuery,
|
|
||||||
expandedQueries,
|
|
||||||
primaryQuery,
|
|
||||||
expansionStatus,
|
|
||||||
expansionSource,
|
|
||||||
expansionError,
|
|
||||||
usedFallbackQuery,
|
|
||||||
runId,
|
|
||||||
workflowId,
|
|
||||||
workflowStatus,
|
|
||||||
billingReserveStatus: 'SKIPPED',
|
|
||||||
billingFinalizeStatus: 'SKIPPED',
|
|
||||||
businessesCount: 0,
|
|
||||||
contactsCount: 0,
|
|
||||||
uniqueContactDomains: 0,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import type { EnvConfig as BaseEnvConfig } from '@clawd/auth-runtime';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query expansion result from LLM or rule-based logic
|
* Query expansion result from LLM or rule-based logic
|
||||||
*/
|
*/
|
||||||
|
|
@ -45,10 +43,3 @@ export interface OutputResult {
|
||||||
uniqueContactDomains: number;
|
uniqueContactDomains: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Client-finder specific environment configuration
|
|
||||||
* Extends the shared auth config with skill-specific fields
|
|
||||||
*/
|
|
||||||
export interface EnvConfig extends BaseEnvConfig {
|
|
||||||
queryExpansionJson: string;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { requestApiWithAutoRefresh } from '@clawd/auth-runtime';
|
import type { SkillClient } from './auth-cli.ts';
|
||||||
import type { EnvConfig as AuthEnvConfig } from '@clawd/auth-runtime';
|
|
||||||
import { WorkflowStartResponse } from './types.js';
|
import { WorkflowStartResponse } from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,26 +17,15 @@ export function parseWorkflowId(responseBody: string): string {
|
||||||
* Start cold outreach workflow
|
* Start cold outreach workflow
|
||||||
*/
|
*/
|
||||||
export async function startWorkflow(
|
export async function startWorkflow(
|
||||||
config: AuthEnvConfig,
|
client: SkillClient,
|
||||||
dryRun: boolean,
|
|
||||||
accessToken: string,
|
|
||||||
query: string,
|
query: string,
|
||||||
country: string,
|
country: string,
|
||||||
): Promise<{ workflowId: string; error: string }> {
|
): Promise<{ workflowId: string; error: string }> {
|
||||||
const payload = JSON.stringify({
|
const result = await client.post('/ecom/cold-outreach/run-flow', {
|
||||||
query,
|
query,
|
||||||
country,
|
country,
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await requestApiWithAutoRefresh(
|
|
||||||
'POST',
|
|
||||||
`${config.authBase}/ecom/cold-outreach/run-flow`,
|
|
||||||
dryRun,
|
|
||||||
config,
|
|
||||||
payload,
|
|
||||||
accessToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.status < 200 || result.status >= 300) {
|
if (result.status < 200 || result.status >= 300) {
|
||||||
const error = parseError(result.body);
|
const error = parseError(result.body);
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue