@youdotcom-oss/api 0.1.1 → 0.2.1
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.
- package/README.md +36 -36
- package/bin/cli.js +233 -400
- package/package.json +1 -1
- package/src/cli.ts +92 -21
- package/src/contents/contents.schemas.ts +8 -7
- package/src/contents/tests/contents.request.spec.ts +109 -0
- package/src/contents/tests/contents.schema-validation.spec.ts +75 -0
- package/src/deep-search/deep-search.schemas.ts +48 -0
- package/src/deep-search/deep-search.utils.ts +79 -0
- package/src/deep-search/tests/deep-search.request.spec.ts +109 -0
- package/src/deep-search/tests/deep-search.schema-validation.spec.ts +71 -0
- package/src/deep-search/tests/deep-search.utils.docker.ts +139 -0
- package/src/main.ts +4 -3
- package/src/search/search.schemas.ts +65 -6
- package/src/search/search.utils.ts +6 -28
- package/src/search/tests/search.request.spec.ts +122 -0
- package/src/search/tests/search.schema-validation.spec.ts +152 -0
- package/src/search/tests/{search.utils.spec.ts → search.utils.docker.ts} +0 -10
- package/src/shared/api.constants.ts +1 -1
- package/src/shared/check-response-for-errors.ts +1 -1
- package/src/shared/command-runner.ts +95 -0
- package/src/shared/dry-run-utils.ts +141 -0
- package/src/shared/tests/command-runner.spec.ts +210 -0
- package/src/shared/use-get-user-agents.ts +1 -1
- package/src/commands/contents.ts +0 -52
- package/src/commands/express.ts +0 -52
- package/src/commands/search.ts +0 -52
- package/src/express/express.schemas.ts +0 -85
- package/src/express/express.utils.ts +0 -113
- package/src/express/tests/express.utils.spec.ts +0 -83
- /package/src/contents/tests/{contents.utils.spec.ts → contents.utils.docker.ts} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared command infrastructure for CLI commands
|
|
3
|
+
* Handles flag parsing, validation, and execution
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { parseArgs } from 'node:util'
|
|
9
|
+
import * as z from 'zod'
|
|
10
|
+
import type { GetUserAgent } from './api.types.ts'
|
|
11
|
+
import type { DryRunResult } from './dry-run-utils.ts'
|
|
12
|
+
import { useGetUserAgent } from './use-get-user-agents.ts'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for a command
|
|
16
|
+
*
|
|
17
|
+
* @typeParam TInput - Zod-inferred input type
|
|
18
|
+
* @typeParam TOutput - Command output type
|
|
19
|
+
*/
|
|
20
|
+
export type CommandConfig<TInput, TOutput> = {
|
|
21
|
+
schema: z.ZodType<TInput>
|
|
22
|
+
handler: (params: { input: TInput; YDC_API_KEY: string; getUserAgent: GetUserAgent }) => Promise<TOutput>
|
|
23
|
+
dryRunHandler?: (params: { input: TInput; YDC_API_KEY: string; getUserAgent: GetUserAgent }) => DryRunResult
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run a command with standardized flag parsing and validation
|
|
28
|
+
* Handles --schema, --json, --api-key, --client, and --dry-run flags
|
|
29
|
+
*
|
|
30
|
+
* @param args - Command line arguments
|
|
31
|
+
* @param config - Command configuration with schema and handler
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export const runCommand = async <TInput, TOutput>(args: string[], config: CommandConfig<TInput, TOutput>) => {
|
|
36
|
+
// Handle --schema flag
|
|
37
|
+
if (args.includes('--schema')) {
|
|
38
|
+
console.log(JSON.stringify(z.toJSONSchema(config.schema)))
|
|
39
|
+
process.exit(0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse flags with Node's built-in parseArgs
|
|
43
|
+
const { values } = parseArgs({
|
|
44
|
+
args,
|
|
45
|
+
options: {
|
|
46
|
+
json: { type: 'string' },
|
|
47
|
+
'api-key': { type: 'string' },
|
|
48
|
+
client: { type: 'string' },
|
|
49
|
+
'dry-run': { type: 'boolean' },
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// --json is required
|
|
54
|
+
if (!values.json) {
|
|
55
|
+
throw new Error('--json flag is required')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Parse JSON input
|
|
59
|
+
const input = JSON.parse(values.json)
|
|
60
|
+
const apiKey = values['api-key']
|
|
61
|
+
const client = values.client || process.env.YDC_CLIENT
|
|
62
|
+
|
|
63
|
+
// Get API key from options or environment
|
|
64
|
+
const YDC_API_KEY = apiKey || process.env.YDC_API_KEY
|
|
65
|
+
if (!YDC_API_KEY) {
|
|
66
|
+
throw new Error('YDC_API_KEY environment variable is required')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate with schema
|
|
70
|
+
const validatedInput = config.schema.parse(input)
|
|
71
|
+
|
|
72
|
+
// Create getUserAgent function
|
|
73
|
+
const getUserAgent = useGetUserAgent(client)
|
|
74
|
+
|
|
75
|
+
// Handle --dry-run flag
|
|
76
|
+
if (values['dry-run'] && config.dryRunHandler) {
|
|
77
|
+
const dryRunResult = config.dryRunHandler({
|
|
78
|
+
input: validatedInput,
|
|
79
|
+
YDC_API_KEY,
|
|
80
|
+
getUserAgent,
|
|
81
|
+
})
|
|
82
|
+
console.log(JSON.stringify(dryRunResult))
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Execute handler
|
|
87
|
+
const response = await config.handler({
|
|
88
|
+
input: validatedInput,
|
|
89
|
+
YDC_API_KEY,
|
|
90
|
+
getUserAgent,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Output response to stdout (success)
|
|
94
|
+
console.log(JSON.stringify(response))
|
|
95
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dry-run utilities for testing request construction without API calls
|
|
3
|
+
* These functions build request details (URL, headers, body) that can be inspected
|
|
4
|
+
* without making actual API calls.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ContentsQuery } from '../contents/contents.schemas.ts'
|
|
10
|
+
import type { DeepSearchQuery } from '../deep-search/deep-search.schemas.ts'
|
|
11
|
+
import type { SearchQuery } from '../search/search.schemas.ts'
|
|
12
|
+
import { CONTENTS_API_URL, DEEP_SEARCH_API_URL, SEARCH_API_URL } from './api.constants.ts'
|
|
13
|
+
import type { GetUserAgent } from './api.types.ts'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Result structure for dry-run request inspection
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type DryRunResult = {
|
|
21
|
+
url: string
|
|
22
|
+
method: 'GET' | 'POST'
|
|
23
|
+
headers: Record<string, string>
|
|
24
|
+
body?: string
|
|
25
|
+
queryParams?: Record<string, string>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build search request details without making API call
|
|
30
|
+
* Useful for testing and debugging query construction
|
|
31
|
+
*
|
|
32
|
+
* @param params - Search query parameters
|
|
33
|
+
* @returns Request details including URL, headers, and query params
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export const buildSearchRequest = ({
|
|
38
|
+
searchQuery,
|
|
39
|
+
YDC_API_KEY,
|
|
40
|
+
getUserAgent,
|
|
41
|
+
}: {
|
|
42
|
+
searchQuery: SearchQuery
|
|
43
|
+
YDC_API_KEY: string
|
|
44
|
+
getUserAgent: GetUserAgent
|
|
45
|
+
}): DryRunResult => {
|
|
46
|
+
// Convert all search query params to query string parameters
|
|
47
|
+
const queryParams: Record<string, string> = {}
|
|
48
|
+
|
|
49
|
+
for (const [name, value] of Object.entries(searchQuery)) {
|
|
50
|
+
if (value !== undefined && value !== null) {
|
|
51
|
+
queryParams[name] = `${value}`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
url: SEARCH_API_URL,
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: {
|
|
59
|
+
'X-API-Key': YDC_API_KEY,
|
|
60
|
+
'User-Agent': getUserAgent(),
|
|
61
|
+
},
|
|
62
|
+
queryParams,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build contents request details without making API call
|
|
68
|
+
* Useful for testing and debugging POST body construction
|
|
69
|
+
*
|
|
70
|
+
* @param params - Contents query parameters
|
|
71
|
+
* @returns Request details including URL, headers, and POST body
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export const buildContentsRequest = ({
|
|
76
|
+
contentsQuery: { urls, formats, format, crawl_timeout },
|
|
77
|
+
YDC_API_KEY,
|
|
78
|
+
getUserAgent,
|
|
79
|
+
}: {
|
|
80
|
+
contentsQuery: ContentsQuery
|
|
81
|
+
YDC_API_KEY: string
|
|
82
|
+
getUserAgent: GetUserAgent
|
|
83
|
+
}): DryRunResult => {
|
|
84
|
+
// Handle backward compatibility: prefer formats array, fallback to format string, default to ['markdown']
|
|
85
|
+
const requestFormats = formats || (format ? [format] : ['markdown'])
|
|
86
|
+
|
|
87
|
+
// Build request body
|
|
88
|
+
const requestBody: {
|
|
89
|
+
urls: string[]
|
|
90
|
+
formats: string[]
|
|
91
|
+
crawl_timeout?: number
|
|
92
|
+
} = {
|
|
93
|
+
urls,
|
|
94
|
+
formats: requestFormats,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (crawl_timeout !== undefined) {
|
|
98
|
+
requestBody.crawl_timeout = crawl_timeout
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
url: CONTENTS_API_URL,
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'X-API-Key': YDC_API_KEY,
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'User-Agent': getUserAgent(),
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(requestBody),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build deep-search request details without making API call
|
|
115
|
+
* Useful for testing and debugging POST body construction
|
|
116
|
+
*
|
|
117
|
+
* @param params - Deep-search query parameters
|
|
118
|
+
* @returns Request details including URL, headers, and POST body
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
export const buildDeepSearchRequest = ({
|
|
123
|
+
deepSearchQuery,
|
|
124
|
+
YDC_API_KEY,
|
|
125
|
+
getUserAgent,
|
|
126
|
+
}: {
|
|
127
|
+
deepSearchQuery: DeepSearchQuery
|
|
128
|
+
YDC_API_KEY: string
|
|
129
|
+
getUserAgent: GetUserAgent
|
|
130
|
+
}): DryRunResult => {
|
|
131
|
+
return {
|
|
132
|
+
url: DEEP_SEARCH_API_URL,
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'X-API-Key': YDC_API_KEY,
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
'User-Agent': getUserAgent(),
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(deepSearchQuery),
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test'
|
|
2
|
+
import * as z from 'zod'
|
|
3
|
+
import { runCommand } from '../command-runner.ts'
|
|
4
|
+
|
|
5
|
+
describe('runCommand', () => {
|
|
6
|
+
const TestSchema = z.object({
|
|
7
|
+
value: z.string(),
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
let originalApiKey: string | undefined
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
// Save original API key
|
|
14
|
+
originalApiKey = process.env.YDC_API_KEY
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterAll(() => {
|
|
18
|
+
// Restore original API key
|
|
19
|
+
if (originalApiKey) {
|
|
20
|
+
process.env.YDC_API_KEY = originalApiKey
|
|
21
|
+
} else {
|
|
22
|
+
delete process.env.YDC_API_KEY
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('outputs JSON schema when --schema flag is provided', async () => {
|
|
27
|
+
const originalExit = process.exit
|
|
28
|
+
const originalLog = console.log
|
|
29
|
+
|
|
30
|
+
process.exit = (() => {
|
|
31
|
+
throw new Error('EXIT')
|
|
32
|
+
}) as typeof process.exit
|
|
33
|
+
|
|
34
|
+
let outputData = ''
|
|
35
|
+
console.log = (data: string) => {
|
|
36
|
+
outputData = data
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await runCommand(['--schema'], {
|
|
41
|
+
schema: TestSchema,
|
|
42
|
+
handler: async () => ({ result: 'test' }),
|
|
43
|
+
})
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error instanceof Error && error.message === 'EXIT') {
|
|
46
|
+
const schema = JSON.parse(outputData)
|
|
47
|
+
expect(schema.type).toBe('object')
|
|
48
|
+
expect(schema.properties).toBeDefined()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Restore original functions
|
|
53
|
+
process.exit = originalExit
|
|
54
|
+
console.log = originalLog
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('throws error when --json flag is missing', async () => {
|
|
58
|
+
expect(async () => {
|
|
59
|
+
await runCommand([], {
|
|
60
|
+
schema: TestSchema,
|
|
61
|
+
handler: async () => ({ result: 'test' }),
|
|
62
|
+
})
|
|
63
|
+
}).toThrow('--json flag is required')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('throws error when JSON is malformed', async () => {
|
|
67
|
+
expect(async () => {
|
|
68
|
+
await runCommand(['--json', 'invalid-json'], {
|
|
69
|
+
schema: TestSchema,
|
|
70
|
+
handler: async () => ({ result: 'test' }),
|
|
71
|
+
})
|
|
72
|
+
}).toThrow()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('throws error when API key is missing from both flag and env', async () => {
|
|
76
|
+
const originalApiKey = process.env.YDC_API_KEY
|
|
77
|
+
delete process.env.YDC_API_KEY
|
|
78
|
+
|
|
79
|
+
expect(async () => {
|
|
80
|
+
await runCommand(['--json', '{"value":"test"}'], {
|
|
81
|
+
schema: TestSchema,
|
|
82
|
+
handler: async () => ({ result: 'test' }),
|
|
83
|
+
})
|
|
84
|
+
}).toThrow('YDC_API_KEY environment variable is required')
|
|
85
|
+
|
|
86
|
+
// Restore original value
|
|
87
|
+
if (originalApiKey) {
|
|
88
|
+
process.env.YDC_API_KEY = originalApiKey
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('resolves API key from --api-key flag over environment', async () => {
|
|
93
|
+
const originalLog = console.log
|
|
94
|
+
process.env.YDC_API_KEY = 'env-key'
|
|
95
|
+
|
|
96
|
+
let capturedKey = ''
|
|
97
|
+
console.log = () => {
|
|
98
|
+
// Suppress output
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await runCommand(['--json', '{"value":"test"}', '--api-key', 'flag-key'], {
|
|
102
|
+
schema: TestSchema,
|
|
103
|
+
handler: async ({ YDC_API_KEY }) => {
|
|
104
|
+
capturedKey = YDC_API_KEY
|
|
105
|
+
return { result: 'test' }
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
expect(capturedKey).toBe('flag-key')
|
|
110
|
+
|
|
111
|
+
// Restore original function
|
|
112
|
+
console.log = originalLog
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('throws error when schema validation fails', async () => {
|
|
116
|
+
process.env.YDC_API_KEY = 'test-key'
|
|
117
|
+
|
|
118
|
+
expect(async () => {
|
|
119
|
+
await runCommand(['--json', '{"invalid":"field"}'], {
|
|
120
|
+
schema: TestSchema,
|
|
121
|
+
handler: async () => ({ result: 'test' }),
|
|
122
|
+
})
|
|
123
|
+
}).toThrow()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('calls handler when all validations pass', async () => {
|
|
127
|
+
const originalLog = console.log
|
|
128
|
+
process.env.YDC_API_KEY = 'test-key'
|
|
129
|
+
|
|
130
|
+
let handlerCalled = false
|
|
131
|
+
let outputData = ''
|
|
132
|
+
console.log = (data: string) => {
|
|
133
|
+
outputData = data
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await runCommand(['--json', '{"value":"test"}'], {
|
|
137
|
+
schema: TestSchema,
|
|
138
|
+
handler: async () => {
|
|
139
|
+
handlerCalled = true
|
|
140
|
+
return { result: 'success' }
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
expect(handlerCalled).toBe(true)
|
|
145
|
+
const output = JSON.parse(outputData)
|
|
146
|
+
expect(output.result).toBe('success')
|
|
147
|
+
|
|
148
|
+
// Restore original function
|
|
149
|
+
console.log = originalLog
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('calls dryRunHandler when --dry-run flag is provided', async () => {
|
|
153
|
+
const originalLog = console.log
|
|
154
|
+
process.env.YDC_API_KEY = 'test-key'
|
|
155
|
+
|
|
156
|
+
let dryRunCalled = false
|
|
157
|
+
let handlerCalled = false
|
|
158
|
+
let outputData = ''
|
|
159
|
+
console.log = (data: string) => {
|
|
160
|
+
outputData = data
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await runCommand(['--json', '{"value":"test"}', '--dry-run'], {
|
|
164
|
+
schema: TestSchema,
|
|
165
|
+
handler: async () => {
|
|
166
|
+
handlerCalled = true
|
|
167
|
+
return { result: 'success' }
|
|
168
|
+
},
|
|
169
|
+
dryRunHandler: () => {
|
|
170
|
+
dryRunCalled = true
|
|
171
|
+
return {
|
|
172
|
+
url: 'https://test.com',
|
|
173
|
+
method: 'GET',
|
|
174
|
+
headers: { 'X-API-Key': 'test-key' },
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
expect(dryRunCalled).toBe(true)
|
|
180
|
+
expect(handlerCalled).toBe(false)
|
|
181
|
+
const output = JSON.parse(outputData)
|
|
182
|
+
expect(output.url).toBe('https://test.com')
|
|
183
|
+
|
|
184
|
+
// Restore original function
|
|
185
|
+
console.log = originalLog
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test('respects --client flag for User-Agent', async () => {
|
|
189
|
+
const originalLog = console.log
|
|
190
|
+
process.env.YDC_API_KEY = 'test-key'
|
|
191
|
+
|
|
192
|
+
let capturedUserAgent = ''
|
|
193
|
+
console.log = () => {
|
|
194
|
+
// Suppress output
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
await runCommand(['--json', '{"value":"test"}', '--client', 'TestClient'], {
|
|
198
|
+
schema: TestSchema,
|
|
199
|
+
handler: async ({ getUserAgent }) => {
|
|
200
|
+
capturedUserAgent = getUserAgent()
|
|
201
|
+
return { result: 'test' }
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
expect(capturedUserAgent).toContain('TestClient')
|
|
206
|
+
|
|
207
|
+
// Restore original function
|
|
208
|
+
console.log = originalLog
|
|
209
|
+
})
|
|
210
|
+
})
|
package/src/commands/contents.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { parseArgs } from 'node:util'
|
|
2
|
-
import * as z from 'zod'
|
|
3
|
-
import { ContentsQuerySchema } from '../contents/contents.schemas.ts'
|
|
4
|
-
import { fetchContents } from '../contents/contents.utils.ts'
|
|
5
|
-
import { useGetUserAgent } from '../shared/use-get-user-agents.ts'
|
|
6
|
-
|
|
7
|
-
export const contentsCommand = async (args: string[]) => {
|
|
8
|
-
// Handle --schema flag
|
|
9
|
-
if (args.includes('--schema')) {
|
|
10
|
-
console.log(JSON.stringify(z.toJSONSchema(ContentsQuerySchema)))
|
|
11
|
-
process.exit(0)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Parse flags with Node's built-in parseArgs
|
|
15
|
-
const { values } = parseArgs({
|
|
16
|
-
args,
|
|
17
|
-
options: {
|
|
18
|
-
json: { type: 'string' },
|
|
19
|
-
'api-key': { type: 'string' },
|
|
20
|
-
client: { type: 'string' },
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// --json is required
|
|
25
|
-
if (!values.json) {
|
|
26
|
-
throw new Error('--json flag is required')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Parse JSON and validate with schema
|
|
30
|
-
const query = JSON.parse(values.json)
|
|
31
|
-
const apiKey = values['api-key']
|
|
32
|
-
const client = values.client || process.env.YDC_CLIENT
|
|
33
|
-
|
|
34
|
-
// Get API key from options or environment
|
|
35
|
-
const YDC_API_KEY = apiKey || process.env.YDC_API_KEY
|
|
36
|
-
if (!YDC_API_KEY) {
|
|
37
|
-
throw new Error('YDC_API_KEY environment variable is required')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate with schema (includes urls validation)
|
|
41
|
-
const contentsQuery = ContentsQuerySchema.parse(query)
|
|
42
|
-
|
|
43
|
-
// Fetch contents
|
|
44
|
-
const response = await fetchContents({
|
|
45
|
-
contentsQuery,
|
|
46
|
-
YDC_API_KEY,
|
|
47
|
-
getUserAgent: useGetUserAgent(client),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// Output response to stdout (success)
|
|
51
|
-
console.log(JSON.stringify(response))
|
|
52
|
-
}
|
package/src/commands/express.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { parseArgs } from 'node:util'
|
|
2
|
-
import * as z from 'zod'
|
|
3
|
-
import { ExpressAgentInputSchema } from '../express/express.schemas.ts'
|
|
4
|
-
import { callExpressAgent } from '../express/express.utils.ts'
|
|
5
|
-
import { useGetUserAgent } from '../shared/use-get-user-agents.ts'
|
|
6
|
-
|
|
7
|
-
export const expressCommand = async (args: string[]) => {
|
|
8
|
-
// Handle --schema flag
|
|
9
|
-
if (args.includes('--schema')) {
|
|
10
|
-
console.log(JSON.stringify(z.toJSONSchema(ExpressAgentInputSchema)))
|
|
11
|
-
process.exit(0)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Parse flags with Node's built-in parseArgs
|
|
15
|
-
const { values } = parseArgs({
|
|
16
|
-
args,
|
|
17
|
-
options: {
|
|
18
|
-
json: { type: 'string' },
|
|
19
|
-
'api-key': { type: 'string' },
|
|
20
|
-
client: { type: 'string' },
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// --json is required
|
|
25
|
-
if (!values.json) {
|
|
26
|
-
throw new Error('--json flag is required')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Parse JSON and validate with schema
|
|
30
|
-
const input = JSON.parse(values.json)
|
|
31
|
-
const apiKey = values['api-key']
|
|
32
|
-
const client = values.client || process.env.YDC_CLIENT
|
|
33
|
-
|
|
34
|
-
// Get API key from options or environment
|
|
35
|
-
const YDC_API_KEY = apiKey || process.env.YDC_API_KEY
|
|
36
|
-
if (!YDC_API_KEY) {
|
|
37
|
-
throw new Error('YDC_API_KEY environment variable is required')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate with schema (includes input validation)
|
|
41
|
-
const agentInput = ExpressAgentInputSchema.parse(input)
|
|
42
|
-
|
|
43
|
-
// Call agent
|
|
44
|
-
const response = await callExpressAgent({
|
|
45
|
-
agentInput,
|
|
46
|
-
YDC_API_KEY,
|
|
47
|
-
getUserAgent: useGetUserAgent(client),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// Output response to stdout (success)
|
|
51
|
-
console.log(JSON.stringify(response))
|
|
52
|
-
}
|
package/src/commands/search.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { parseArgs } from 'node:util'
|
|
2
|
-
import * as z from 'zod'
|
|
3
|
-
import { SearchQuerySchema } from '../search/search.schemas.ts'
|
|
4
|
-
import { fetchSearchResults } from '../search/search.utils.ts'
|
|
5
|
-
import { useGetUserAgent } from '../shared/use-get-user-agents.ts'
|
|
6
|
-
|
|
7
|
-
export const searchCommand = async (args: string[]) => {
|
|
8
|
-
// Handle --schema flag
|
|
9
|
-
if (args.includes('--schema')) {
|
|
10
|
-
console.log(JSON.stringify(z.toJSONSchema(SearchQuerySchema)))
|
|
11
|
-
process.exit(0)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Parse flags with Node's built-in parseArgs
|
|
15
|
-
const { values } = parseArgs({
|
|
16
|
-
args,
|
|
17
|
-
options: {
|
|
18
|
-
json: { type: 'string' },
|
|
19
|
-
'api-key': { type: 'string' },
|
|
20
|
-
client: { type: 'string' },
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// --json is required
|
|
25
|
-
if (!values.json) {
|
|
26
|
-
throw new Error('--json flag is required')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Parse JSON and validate with schema
|
|
30
|
-
const query = JSON.parse(values.json)
|
|
31
|
-
const apiKey = values['api-key']
|
|
32
|
-
const client = values.client || process.env.YDC_CLIENT
|
|
33
|
-
|
|
34
|
-
// Get API key from options or environment
|
|
35
|
-
const YDC_API_KEY = apiKey || process.env.YDC_API_KEY
|
|
36
|
-
if (!YDC_API_KEY) {
|
|
37
|
-
throw new Error('YDC_API_KEY environment variable is required')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate with schema (includes query validation)
|
|
41
|
-
const searchQuery = SearchQuerySchema.parse(query)
|
|
42
|
-
|
|
43
|
-
// Fetch results
|
|
44
|
-
const response = await fetchSearchResults({
|
|
45
|
-
searchQuery,
|
|
46
|
-
YDC_API_KEY,
|
|
47
|
-
getUserAgent: useGetUserAgent(client),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// Output response to stdout (success)
|
|
51
|
-
console.log(JSON.stringify(response))
|
|
52
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import * as z from 'zod'
|
|
2
|
-
|
|
3
|
-
export const ExpressAgentInputSchema = z.object({
|
|
4
|
-
input: z.string().min(1, 'Input is required').describe('Query or prompt'),
|
|
5
|
-
tools: z
|
|
6
|
-
.array(
|
|
7
|
-
z.object({
|
|
8
|
-
type: z.enum(['web_search']).describe('Tool type'),
|
|
9
|
-
}),
|
|
10
|
-
)
|
|
11
|
-
.optional()
|
|
12
|
-
.describe('Tools (web search only)'),
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
export type ExpressAgentInput = z.infer<typeof ExpressAgentInputSchema>
|
|
16
|
-
|
|
17
|
-
// API Response Schema - Validates the full response from You.com API
|
|
18
|
-
|
|
19
|
-
// Search result content item from web_search.results
|
|
20
|
-
// Note: thumbnail_url, source_type, and provider are API-only pass-through fields not used in MCP output
|
|
21
|
-
const ApiSearchResultItemSchema = z.object({
|
|
22
|
-
source_type: z.string().nullable().optional(),
|
|
23
|
-
citation_uri: z.string().optional(), // Used as fallback for url in transformation
|
|
24
|
-
url: z.string(),
|
|
25
|
-
title: z.string(),
|
|
26
|
-
snippet: z.string(),
|
|
27
|
-
thumbnail_url: z.string().nullable().optional(), // API-only, not transformed to MCP output
|
|
28
|
-
provider: z.string().nullable().optional(), // API-only, not transformed to MCP output
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
// Union of possible output item types from API
|
|
32
|
-
const ExpressAgentApiOutputItemSchema = z.union([
|
|
33
|
-
// web_search.results type - has content array, no text
|
|
34
|
-
z.object({
|
|
35
|
-
type: z.literal('web_search.results'),
|
|
36
|
-
content: z.array(ApiSearchResultItemSchema),
|
|
37
|
-
}),
|
|
38
|
-
// message.answer type - has text, no content
|
|
39
|
-
z.object({
|
|
40
|
-
type: z.literal('message.answer'),
|
|
41
|
-
text: z.string(),
|
|
42
|
-
}),
|
|
43
|
-
])
|
|
44
|
-
|
|
45
|
-
export const ExpressAgentApiResponseSchema = z
|
|
46
|
-
.object({
|
|
47
|
-
output: z.array(ExpressAgentApiOutputItemSchema),
|
|
48
|
-
agent: z.string().optional().describe('Agent identifier'),
|
|
49
|
-
mode: z.string().optional().describe('Agent mode'),
|
|
50
|
-
input: z
|
|
51
|
-
.array(
|
|
52
|
-
z.object({
|
|
53
|
-
role: z.enum(['user']).describe('User role'),
|
|
54
|
-
content: z.string().describe('User question'),
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
.optional()
|
|
58
|
-
.describe('Input messages'),
|
|
59
|
-
})
|
|
60
|
-
.passthrough()
|
|
61
|
-
|
|
62
|
-
export type ExpressAgentApiResponse = z.infer<typeof ExpressAgentApiResponseSchema>
|
|
63
|
-
|
|
64
|
-
// MCP Output Schema - Defines what we return to the MCP client (answer + optional search results, token efficient)
|
|
65
|
-
|
|
66
|
-
// Search result item for MCP output
|
|
67
|
-
const McpSearchResultItemSchema = z.object({
|
|
68
|
-
url: z.string().describe('URL'),
|
|
69
|
-
title: z.string().describe('Title'),
|
|
70
|
-
snippet: z.string().describe('Snippet'),
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// MCP response structure: answer (always) + results (optional when web_search used)
|
|
74
|
-
const ExpressAgentMcpResponseSchema = z.object({
|
|
75
|
-
answer: z.string().describe('AI answer'),
|
|
76
|
-
results: z
|
|
77
|
-
.object({
|
|
78
|
-
web: z.array(McpSearchResultItemSchema).describe('Web results'),
|
|
79
|
-
})
|
|
80
|
-
.optional()
|
|
81
|
-
.describe('Search results'),
|
|
82
|
-
agent: z.string().optional().describe('Agent ID'),
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
export type ExpressAgentMcpResponse = z.infer<typeof ExpressAgentMcpResponseSchema>
|