@foundation0/api 1.1.12 → 1.1.13
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 +128 -128
- package/agents.ts +918 -918
- package/git.ts +20 -20
- package/libs/curl.ts +770 -770
- package/mcp/cli.mjs +37 -37
- package/mcp/cli.ts +87 -87
- package/mcp/client.ts +565 -565
- package/mcp/index.ts +15 -15
- package/mcp/server.ts +2991 -2991
- package/net.ts +170 -170
- package/package.json +13 -9
- package/projects.ts +4250 -4340
- package/taskgraph-parser.ts +217 -217
- package/libs/curl.test.ts +0 -130
- package/mcp/AGENTS.md +0 -130
- package/mcp/client.test.ts +0 -142
- package/mcp/manual.md +0 -179
- package/mcp/server.test.ts +0 -967
package/net.ts
CHANGED
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
import { curl as curlImpl, type CurlResult, type CurlToolOptions } from './libs/curl'
|
|
2
|
-
|
|
3
|
-
export type NetCurlResult = CurlResult
|
|
4
|
-
|
|
5
|
-
type NetCurlRequest = {
|
|
6
|
-
url?: string | string[]
|
|
7
|
-
urls?: string[]
|
|
8
|
-
method?: string
|
|
9
|
-
headers?: Record<string, string>
|
|
10
|
-
body?: string
|
|
11
|
-
data?: string | string[]
|
|
12
|
-
json?: unknown
|
|
13
|
-
form?: Record<string, string>
|
|
14
|
-
followRedirects?: boolean
|
|
15
|
-
location?: boolean
|
|
16
|
-
maxRedirects?: number
|
|
17
|
-
includeHeaders?: boolean
|
|
18
|
-
head?: boolean
|
|
19
|
-
user?: string
|
|
20
|
-
output?: string
|
|
21
|
-
remoteName?: boolean
|
|
22
|
-
fail?: boolean
|
|
23
|
-
silent?: boolean
|
|
24
|
-
showError?: boolean
|
|
25
|
-
verbose?: boolean
|
|
26
|
-
writeOut?: string
|
|
27
|
-
maxTimeSeconds?: number
|
|
28
|
-
get?: boolean
|
|
29
|
-
timeoutMs?: number
|
|
30
|
-
cwd?: string
|
|
31
|
-
env?: Record<string, string>
|
|
32
|
-
stdin?: string | Uint8Array | number[]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
36
|
-
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
37
|
-
|
|
38
|
-
const toStringRecord = (value: unknown): Record<string, string> | null => {
|
|
39
|
-
if (!isRecord(value)) return null
|
|
40
|
-
const out: Record<string, string> = {}
|
|
41
|
-
for (const [k, v] of Object.entries(value)) {
|
|
42
|
-
if (v === undefined || v === null) continue
|
|
43
|
-
out[k] = String(v)
|
|
44
|
-
}
|
|
45
|
-
return out
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const parseRequest = (value: unknown): NetCurlRequest | null => {
|
|
49
|
-
if (!isRecord(value)) return null
|
|
50
|
-
if ('args' in value) return null
|
|
51
|
-
if (!('url' in value) && !('urls' in value)) return null
|
|
52
|
-
return value as NetCurlRequest
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const toUint8Array = (value: unknown): Uint8Array | undefined => {
|
|
56
|
-
if (value instanceof Uint8Array) return value
|
|
57
|
-
if (Array.isArray(value)) {
|
|
58
|
-
const bytes = value.map((entry) => Number(entry)).filter((n) => Number.isFinite(n) && n >= 0 && n <= 255)
|
|
59
|
-
return new Uint8Array(bytes)
|
|
60
|
-
}
|
|
61
|
-
return undefined
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const buildCurlArgsFromRequest = (request: NetCurlRequest): { args: string[]; toolOptions: CurlToolOptions } => {
|
|
65
|
-
const args: string[] = []
|
|
66
|
-
|
|
67
|
-
const urls = (() => {
|
|
68
|
-
if (Array.isArray(request.urls)) return request.urls.map(String)
|
|
69
|
-
if (Array.isArray(request.url)) return request.url.map(String)
|
|
70
|
-
if (typeof request.url === 'string') return [request.url]
|
|
71
|
-
return []
|
|
72
|
-
})()
|
|
73
|
-
|
|
74
|
-
const method = typeof request.method === 'string' && request.method.trim().length > 0
|
|
75
|
-
? request.method.trim().toUpperCase()
|
|
76
|
-
: null
|
|
77
|
-
|
|
78
|
-
if (request.includeHeaders === true) args.push('-i')
|
|
79
|
-
if (request.head === true || method === 'HEAD') args.push('-I')
|
|
80
|
-
|
|
81
|
-
if (request.followRedirects === true || request.location === true) args.push('-L')
|
|
82
|
-
if (typeof request.maxRedirects === 'number' && Number.isInteger(request.maxRedirects) && request.maxRedirects >= 0) {
|
|
83
|
-
args.push('--max-redirs', String(request.maxRedirects))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (request.fail === true) args.push('-f')
|
|
87
|
-
if (request.silent === true) args.push('-s')
|
|
88
|
-
if (request.showError === true) args.push('-S')
|
|
89
|
-
if (request.verbose === true) args.push('-v')
|
|
90
|
-
|
|
91
|
-
if (request.get === true) args.push('-G')
|
|
92
|
-
|
|
93
|
-
if (typeof request.user === 'string' && request.user.trim().length > 0) {
|
|
94
|
-
args.push('-u', request.user.trim())
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const headers = toStringRecord(request.headers)
|
|
98
|
-
if (headers) {
|
|
99
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
100
|
-
args.push('-H', `${k}: ${v}`)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (typeof request.writeOut === 'string') args.push('-w', request.writeOut)
|
|
105
|
-
if (typeof request.output === 'string' && request.output.trim().length > 0) args.push('-o', request.output.trim())
|
|
106
|
-
if (request.remoteName === true) args.push('-O')
|
|
107
|
-
|
|
108
|
-
if (typeof request.maxTimeSeconds === 'number' && Number.isFinite(request.maxTimeSeconds) && request.maxTimeSeconds > 0) {
|
|
109
|
-
args.push('--max-time', String(request.maxTimeSeconds))
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (method && method !== 'HEAD' && request.head !== true) {
|
|
113
|
-
args.push('-X', method)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (typeof request.body === 'string') {
|
|
117
|
-
args.push('--data-raw', request.body)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const data = request.data
|
|
121
|
-
if (typeof data === 'string') {
|
|
122
|
-
args.push('-d', data)
|
|
123
|
-
} else if (Array.isArray(data)) {
|
|
124
|
-
for (const entry of data) args.push('-d', String(entry))
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (request.json !== undefined) {
|
|
128
|
-
const rendered = typeof request.json === 'string' ? request.json : JSON.stringify(request.json)
|
|
129
|
-
args.push('--json', rendered)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (isRecord(request.form)) {
|
|
133
|
-
for (const [k, v] of Object.entries(request.form)) {
|
|
134
|
-
if (v === undefined || v === null) continue
|
|
135
|
-
args.push('-F', `${k}=${String(v)}`)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
args.push(...urls)
|
|
140
|
-
|
|
141
|
-
const toolOptions: CurlToolOptions = {
|
|
142
|
-
timeoutMs: typeof request.timeoutMs === 'number' && Number.isFinite(request.timeoutMs) && request.timeoutMs > 0
|
|
143
|
-
? request.timeoutMs
|
|
144
|
-
: undefined,
|
|
145
|
-
cwd: typeof request.cwd === 'string' && request.cwd.trim().length > 0 ? request.cwd.trim() : undefined,
|
|
146
|
-
env: toStringRecord(request.env) ?? undefined,
|
|
147
|
-
stdin: typeof request.stdin === 'string' ? request.stdin : toUint8Array(request.stdin),
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return { args, toolOptions }
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export const curl = async (...rawArgs: unknown[]): Promise<CurlResult> => {
|
|
154
|
-
const request = rawArgs.length === 1 ? parseRequest(rawArgs[0]) : null
|
|
155
|
-
if (request) {
|
|
156
|
-
const { args, toolOptions } = buildCurlArgsFromRequest(request)
|
|
157
|
-
return curlImpl(...args, toolOptions)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const stringArgCount = rawArgs.filter((arg) => typeof arg === 'string').length
|
|
161
|
-
if (stringArgCount === 0 && rawArgs.length === 1 && isRecord(rawArgs[0])) {
|
|
162
|
-
const maybe = rawArgs[0] as Record<string, unknown>
|
|
163
|
-
if ('method' in maybe || 'headers' in maybe || 'body' in maybe || 'data' in maybe || 'json' in maybe) {
|
|
164
|
-
const { args, toolOptions } = buildCurlArgsFromRequest(maybe as NetCurlRequest)
|
|
165
|
-
return curlImpl(...args, toolOptions)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return curlImpl(...rawArgs)
|
|
170
|
-
}
|
|
1
|
+
import { curl as curlImpl, type CurlResult, type CurlToolOptions } from './libs/curl'
|
|
2
|
+
|
|
3
|
+
export type NetCurlResult = CurlResult
|
|
4
|
+
|
|
5
|
+
type NetCurlRequest = {
|
|
6
|
+
url?: string | string[]
|
|
7
|
+
urls?: string[]
|
|
8
|
+
method?: string
|
|
9
|
+
headers?: Record<string, string>
|
|
10
|
+
body?: string
|
|
11
|
+
data?: string | string[]
|
|
12
|
+
json?: unknown
|
|
13
|
+
form?: Record<string, string>
|
|
14
|
+
followRedirects?: boolean
|
|
15
|
+
location?: boolean
|
|
16
|
+
maxRedirects?: number
|
|
17
|
+
includeHeaders?: boolean
|
|
18
|
+
head?: boolean
|
|
19
|
+
user?: string
|
|
20
|
+
output?: string
|
|
21
|
+
remoteName?: boolean
|
|
22
|
+
fail?: boolean
|
|
23
|
+
silent?: boolean
|
|
24
|
+
showError?: boolean
|
|
25
|
+
verbose?: boolean
|
|
26
|
+
writeOut?: string
|
|
27
|
+
maxTimeSeconds?: number
|
|
28
|
+
get?: boolean
|
|
29
|
+
timeoutMs?: number
|
|
30
|
+
cwd?: string
|
|
31
|
+
env?: Record<string, string>
|
|
32
|
+
stdin?: string | Uint8Array | number[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
36
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
37
|
+
|
|
38
|
+
const toStringRecord = (value: unknown): Record<string, string> | null => {
|
|
39
|
+
if (!isRecord(value)) return null
|
|
40
|
+
const out: Record<string, string> = {}
|
|
41
|
+
for (const [k, v] of Object.entries(value)) {
|
|
42
|
+
if (v === undefined || v === null) continue
|
|
43
|
+
out[k] = String(v)
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const parseRequest = (value: unknown): NetCurlRequest | null => {
|
|
49
|
+
if (!isRecord(value)) return null
|
|
50
|
+
if ('args' in value) return null
|
|
51
|
+
if (!('url' in value) && !('urls' in value)) return null
|
|
52
|
+
return value as NetCurlRequest
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const toUint8Array = (value: unknown): Uint8Array | undefined => {
|
|
56
|
+
if (value instanceof Uint8Array) return value
|
|
57
|
+
if (Array.isArray(value)) {
|
|
58
|
+
const bytes = value.map((entry) => Number(entry)).filter((n) => Number.isFinite(n) && n >= 0 && n <= 255)
|
|
59
|
+
return new Uint8Array(bytes)
|
|
60
|
+
}
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const buildCurlArgsFromRequest = (request: NetCurlRequest): { args: string[]; toolOptions: CurlToolOptions } => {
|
|
65
|
+
const args: string[] = []
|
|
66
|
+
|
|
67
|
+
const urls = (() => {
|
|
68
|
+
if (Array.isArray(request.urls)) return request.urls.map(String)
|
|
69
|
+
if (Array.isArray(request.url)) return request.url.map(String)
|
|
70
|
+
if (typeof request.url === 'string') return [request.url]
|
|
71
|
+
return []
|
|
72
|
+
})()
|
|
73
|
+
|
|
74
|
+
const method = typeof request.method === 'string' && request.method.trim().length > 0
|
|
75
|
+
? request.method.trim().toUpperCase()
|
|
76
|
+
: null
|
|
77
|
+
|
|
78
|
+
if (request.includeHeaders === true) args.push('-i')
|
|
79
|
+
if (request.head === true || method === 'HEAD') args.push('-I')
|
|
80
|
+
|
|
81
|
+
if (request.followRedirects === true || request.location === true) args.push('-L')
|
|
82
|
+
if (typeof request.maxRedirects === 'number' && Number.isInteger(request.maxRedirects) && request.maxRedirects >= 0) {
|
|
83
|
+
args.push('--max-redirs', String(request.maxRedirects))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (request.fail === true) args.push('-f')
|
|
87
|
+
if (request.silent === true) args.push('-s')
|
|
88
|
+
if (request.showError === true) args.push('-S')
|
|
89
|
+
if (request.verbose === true) args.push('-v')
|
|
90
|
+
|
|
91
|
+
if (request.get === true) args.push('-G')
|
|
92
|
+
|
|
93
|
+
if (typeof request.user === 'string' && request.user.trim().length > 0) {
|
|
94
|
+
args.push('-u', request.user.trim())
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const headers = toStringRecord(request.headers)
|
|
98
|
+
if (headers) {
|
|
99
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
100
|
+
args.push('-H', `${k}: ${v}`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof request.writeOut === 'string') args.push('-w', request.writeOut)
|
|
105
|
+
if (typeof request.output === 'string' && request.output.trim().length > 0) args.push('-o', request.output.trim())
|
|
106
|
+
if (request.remoteName === true) args.push('-O')
|
|
107
|
+
|
|
108
|
+
if (typeof request.maxTimeSeconds === 'number' && Number.isFinite(request.maxTimeSeconds) && request.maxTimeSeconds > 0) {
|
|
109
|
+
args.push('--max-time', String(request.maxTimeSeconds))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (method && method !== 'HEAD' && request.head !== true) {
|
|
113
|
+
args.push('-X', method)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof request.body === 'string') {
|
|
117
|
+
args.push('--data-raw', request.body)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = request.data
|
|
121
|
+
if (typeof data === 'string') {
|
|
122
|
+
args.push('-d', data)
|
|
123
|
+
} else if (Array.isArray(data)) {
|
|
124
|
+
for (const entry of data) args.push('-d', String(entry))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (request.json !== undefined) {
|
|
128
|
+
const rendered = typeof request.json === 'string' ? request.json : JSON.stringify(request.json)
|
|
129
|
+
args.push('--json', rendered)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isRecord(request.form)) {
|
|
133
|
+
for (const [k, v] of Object.entries(request.form)) {
|
|
134
|
+
if (v === undefined || v === null) continue
|
|
135
|
+
args.push('-F', `${k}=${String(v)}`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
args.push(...urls)
|
|
140
|
+
|
|
141
|
+
const toolOptions: CurlToolOptions = {
|
|
142
|
+
timeoutMs: typeof request.timeoutMs === 'number' && Number.isFinite(request.timeoutMs) && request.timeoutMs > 0
|
|
143
|
+
? request.timeoutMs
|
|
144
|
+
: undefined,
|
|
145
|
+
cwd: typeof request.cwd === 'string' && request.cwd.trim().length > 0 ? request.cwd.trim() : undefined,
|
|
146
|
+
env: toStringRecord(request.env) ?? undefined,
|
|
147
|
+
stdin: typeof request.stdin === 'string' ? request.stdin : toUint8Array(request.stdin),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { args, toolOptions }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const curl = async (...rawArgs: unknown[]): Promise<CurlResult> => {
|
|
154
|
+
const request = rawArgs.length === 1 ? parseRequest(rawArgs[0]) : null
|
|
155
|
+
if (request) {
|
|
156
|
+
const { args, toolOptions } = buildCurlArgsFromRequest(request)
|
|
157
|
+
return curlImpl(...args, toolOptions)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const stringArgCount = rawArgs.filter((arg) => typeof arg === 'string').length
|
|
161
|
+
if (stringArgCount === 0 && rawArgs.length === 1 && isRecord(rawArgs[0])) {
|
|
162
|
+
const maybe = rawArgs[0] as Record<string, unknown>
|
|
163
|
+
if ('method' in maybe || 'headers' in maybe || 'body' in maybe || 'data' in maybe || 'json' in maybe) {
|
|
164
|
+
const { args, toolOptions } = buildCurlArgsFromRequest(maybe as NetCurlRequest)
|
|
165
|
+
return curlImpl(...args, toolOptions)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return curlImpl(...rawArgs)
|
|
170
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foundation0/api",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.13",
|
|
4
4
|
"description": "Foundation 0 API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,24 +21,28 @@
|
|
|
21
21
|
"net.ts",
|
|
22
22
|
"taskgraph-parser.ts",
|
|
23
23
|
"projects.ts",
|
|
24
|
-
"libs",
|
|
25
|
-
"mcp"
|
|
24
|
+
"libs/curl.ts",
|
|
25
|
+
"mcp/cli.mjs",
|
|
26
|
+
"mcp/cli.ts",
|
|
27
|
+
"mcp/client.ts",
|
|
28
|
+
"mcp/index.ts",
|
|
29
|
+
"mcp/server.ts"
|
|
26
30
|
],
|
|
27
31
|
"publishConfig": {
|
|
28
32
|
"access": "public",
|
|
29
33
|
"registry": "https://registry.npmjs.org/"
|
|
30
34
|
},
|
|
31
35
|
"dependencies": {
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
37
|
+
"@foundation0/git": "1.3.3"
|
|
34
38
|
},
|
|
35
39
|
"scripts": {
|
|
36
40
|
"mcp": "bun run mcp/cli.ts",
|
|
37
41
|
"test": "bun test",
|
|
38
42
|
"test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
|
|
39
|
-
"deploy": "pnpm publish --access public",
|
|
40
|
-
"version:patch": "pnpm version patch
|
|
41
|
-
"version:minor": "pnpm version minor
|
|
42
|
-
"version:major": "pnpm version major
|
|
43
|
+
"deploy": "pnpm publish --access public --no-git-checks",
|
|
44
|
+
"version:patch": "pnpm version patch --no-workspaces",
|
|
45
|
+
"version:minor": "pnpm version minor --no-workspaces",
|
|
46
|
+
"version:major": "pnpm version major --no-workspaces"
|
|
43
47
|
}
|
|
44
48
|
}
|