@foundation0/api 1.1.2 → 1.1.4

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/net.ts ADDED
@@ -0,0 +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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foundation0/api",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Foundation 0 API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,8 +18,10 @@
18
18
  "files": [
19
19
  "agents.ts",
20
20
  "git.ts",
21
+ "net.ts",
21
22
  "taskgraph-parser.ts",
22
23
  "projects.ts",
24
+ "libs",
23
25
  "mcp"
24
26
  ],
25
27
  "publishConfig": {
@@ -27,12 +29,12 @@
27
29
  "registry": "https://registry.npmjs.org/"
28
30
  },
29
31
  "dependencies": {
30
- "@foundation0/git": "^1.0.0",
32
+ "@foundation0/git": "^1.2.5",
31
33
  "@modelcontextprotocol/sdk": "^1.13.0"
32
34
  },
33
35
  "scripts": {
34
36
  "mcp": "bun run mcp/cli.ts",
35
- "test": "echo \"Error: no test specified\" && exit 1",
37
+ "test": "bun test",
36
38
  "deploy": "pnpm publish --access public",
37
39
  "version:patch": "pnpm version patch && git commit -am \"Bump version to %s\"",
38
40
  "version:minor": "pnpm version minor && git commit -am \"Bump version to %s\"",