@mokoconsulting/mcp-mokogitea-api 1.2.0
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/.gitattributes +94 -0
- package/.gitmessage +9 -0
- package/.mokogitea/ISSUE_TEMPLATE/adr.md +110 -0
- package/.mokogitea/ISSUE_TEMPLATE/bug_report.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/config.yml +18 -0
- package/.mokogitea/ISSUE_TEMPLATE/documentation.md +52 -0
- package/.mokogitea/ISSUE_TEMPLATE/enterprise_support.md +85 -0
- package/.mokogitea/ISSUE_TEMPLATE/feature_request.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/firewall-request.md +190 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_api_integration.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_connection_issue.md +67 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_tool_request.md +49 -0
- package/.mokogitea/ISSUE_TEMPLATE/question.md +82 -0
- package/.mokogitea/ISSUE_TEMPLATE/rfc.md +126 -0
- package/.mokogitea/ISSUE_TEMPLATE/security.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/version.md +24 -0
- package/.mokogitea/auto-assign.yml +76 -0
- package/.mokogitea/auto-dev-issue.yml +207 -0
- package/.mokogitea/auto-release.yml +337 -0
- package/.mokogitea/branch-protection.yml +251 -0
- package/.mokogitea/changelog-validation.yml +101 -0
- package/.mokogitea/codeql-analysis.yml +115 -0
- package/.mokogitea/copilot-agent.yml +44 -0
- package/.mokogitea/deploy-demo.yml +734 -0
- package/.mokogitea/deploy-dev.yml +700 -0
- package/.mokogitea/enterprise-firewall-setup.yml +758 -0
- package/.mokogitea/manifest.xml +25 -0
- package/.mokogitea/mcp-auto-release.yml +278 -0
- package/.mokogitea/mcp-build-test.yml +65 -0
- package/.mokogitea/mcp-sdk-check.yml +109 -0
- package/.mokogitea/mcp-tool-inventory.yml +61 -0
- package/.mokogitea/pr-branch-check.yml +90 -0
- package/.mokogitea/repository-cleanup.yml +525 -0
- package/.mokogitea/standards-compliance.yml +2614 -0
- package/.mokogitea/sync-version-on-merge.yml +133 -0
- package/.mokogitea/workflows/auto-assign.yml +76 -0
- package/.mokogitea/workflows/auto-bump.yml +66 -0
- package/.mokogitea/workflows/auto-dev-issue.yml +207 -0
- package/.mokogitea/workflows/auto-release.yml +341 -0
- package/.mokogitea/workflows/branch-cleanup.yml +48 -0
- package/.mokogitea/workflows/cascade-dev.yml +10 -0
- package/.mokogitea/workflows/changelog-validation.yml +101 -0
- package/.mokogitea/workflows/ci-generic.yml +204 -0
- package/.mokogitea/workflows/cleanup.yml +87 -0
- package/.mokogitea/workflows/codeql-analysis.yml +115 -0
- package/.mokogitea/workflows/copilot-agent.yml +44 -0
- package/.mokogitea/workflows/deploy-manual.yml +126 -0
- package/.mokogitea/workflows/enterprise-firewall-setup.yml +758 -0
- package/.mokogitea/workflows/gitleaks.yml +96 -0
- package/.mokogitea/workflows/issue-branch.yml +73 -0
- package/.mokogitea/workflows/mcp-auto-release.yml +280 -0
- package/.mokogitea/workflows/mcp-build-test.yml +65 -0
- package/.mokogitea/workflows/mcp-sdk-check.yml +109 -0
- package/.mokogitea/workflows/mcp-tool-inventory.yml +61 -0
- package/.mokogitea/workflows/notify.yml +70 -0
- package/.mokogitea/workflows/npm-publish.yml +51 -0
- package/.mokogitea/workflows/pr-check.yml +508 -0
- package/.mokogitea/workflows/pre-release.yml +11 -0
- package/.mokogitea/workflows/repo-health.yml +711 -0
- package/.mokogitea/workflows/repository-cleanup.yml +525 -0
- package/.mokogitea/workflows/security-audit.yml +82 -0
- package/.mokogitea/workflows/standards-compliance.yml +2614 -0
- package/.mokogitea/workflows/sync-version-on-merge.yml +130 -0
- package/.mokogitea/workflows/update-server.yml +312 -0
- package/CHANGELOG.md +145 -0
- package/CLAUDE.md +43 -0
- package/CONTRIBUTING.md +161 -0
- package/README.md +286 -0
- package/SECURITY.md +91 -0
- package/automation/ci-issue-reporter.sh +237 -0
- package/config.example.json +13 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +104 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +48 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1119 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +16 -0
- package/package.json +34 -0
- package/scripts/setup.mjs +40 -0
- package/src/client.ts +120 -0
- package/src/config.ts +58 -0
- package/src/index.ts +1712 -0
- package/src/types.ts +37 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
4
|
+
#
|
|
5
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
#
|
|
7
|
+
# FILE INFORMATION
|
|
8
|
+
# DEFGROUP: Automation.CI
|
|
9
|
+
# INGROUP: moko-platform.Automation
|
|
10
|
+
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
11
|
+
# PATH: /automation/ci-issue-reporter.sh
|
|
12
|
+
# VERSION: 09.23.00
|
|
13
|
+
# BRIEF: Creates or updates a Gitea issue when a CI gate fails.
|
|
14
|
+
# Deduplicates by searching open issues with the "ci-auto" label
|
|
15
|
+
# whose title matches the gate. If a matching issue exists, a comment
|
|
16
|
+
# is appended instead of opening a duplicate.
|
|
17
|
+
# ============================================================================
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# ── Defaults ────────────────────────────────────────────────────────────────
|
|
22
|
+
GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}"
|
|
23
|
+
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
|
24
|
+
REPO="${GITHUB_REPOSITORY:-}"
|
|
25
|
+
RUN_URL="${GITHUB_SERVER_URL:-${GITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
|
|
26
|
+
LABEL_NAME="ci-auto"
|
|
27
|
+
LABEL_COLOR="#e11d48"
|
|
28
|
+
|
|
29
|
+
GATE=""
|
|
30
|
+
DETAILS=""
|
|
31
|
+
SEVERITY="error"
|
|
32
|
+
WORKFLOW=""
|
|
33
|
+
|
|
34
|
+
# ── Parse arguments ─────────────────────────────────────────────────────────
|
|
35
|
+
usage() {
|
|
36
|
+
cat <<EOF
|
|
37
|
+
Usage: ci-issue-reporter.sh --gate NAME --details TEXT [OPTIONS]
|
|
38
|
+
|
|
39
|
+
Required:
|
|
40
|
+
--gate CI gate name (e.g. "Code Quality", "Self-Health")
|
|
41
|
+
--details Human-readable failure description
|
|
42
|
+
|
|
43
|
+
Optional:
|
|
44
|
+
--severity "error" (default) or "warning"
|
|
45
|
+
--workflow Workflow name for the issue title
|
|
46
|
+
--repo owner/repo (default: \$GITHUB_REPOSITORY)
|
|
47
|
+
--run-url URL to the CI run (auto-detected from env)
|
|
48
|
+
--token Gitea API token (default: \$GITEA_TOKEN)
|
|
49
|
+
--url Gitea base URL (default: \$GITEA_URL)
|
|
50
|
+
EOF
|
|
51
|
+
exit 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--gate) GATE="$2"; shift 2 ;;
|
|
57
|
+
--details) DETAILS="$2"; shift 2 ;;
|
|
58
|
+
--severity) SEVERITY="$2"; shift 2 ;;
|
|
59
|
+
--workflow) WORKFLOW="$2"; shift 2 ;;
|
|
60
|
+
--repo) REPO="$2"; shift 2 ;;
|
|
61
|
+
--run-url) RUN_URL="$2"; shift 2 ;;
|
|
62
|
+
--token) GITEA_TOKEN="$2"; shift 2 ;;
|
|
63
|
+
--url) GITEA_URL="$2"; shift 2 ;;
|
|
64
|
+
-h|--help) usage ;;
|
|
65
|
+
*) echo "Unknown option: $1"; usage ;;
|
|
66
|
+
esac
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
|
|
70
|
+
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
|
|
71
|
+
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: GITEA_TOKEN not set"; exit 1; }
|
|
72
|
+
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
|
|
73
|
+
|
|
74
|
+
API="${GITEA_URL}/api/v1/repos/${REPO}"
|
|
75
|
+
|
|
76
|
+
# ── Build title ─────────────────────────────────────────────────────────────
|
|
77
|
+
if [[ -n "$WORKFLOW" ]]; then
|
|
78
|
+
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
|
|
79
|
+
else
|
|
80
|
+
TITLE="[CI] ${GATE} failed"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# ── Ensure label exists ─────────────────────────────────────────────────────
|
|
84
|
+
ensure_label() {
|
|
85
|
+
local exists
|
|
86
|
+
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
|
|
87
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
88
|
+
"${API}/labels" 2>/dev/null || echo "000")
|
|
89
|
+
|
|
90
|
+
if [[ "$exists" == "200" ]]; then
|
|
91
|
+
# Check if label already exists
|
|
92
|
+
local found
|
|
93
|
+
found=$(curl -sf \
|
|
94
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
95
|
+
"${API}/labels" 2>/dev/null \
|
|
96
|
+
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
|
|
97
|
+
|
|
98
|
+
if [[ -z "$found" ]]; then
|
|
99
|
+
curl -sf -X POST \
|
|
100
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
101
|
+
-H "Content-Type: application/json" \
|
|
102
|
+
"${API}/labels" \
|
|
103
|
+
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
|
|
104
|
+
> /dev/null 2>&1 || true
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# ── Search for existing open issue ──────────────────────────────────────────
|
|
110
|
+
find_existing_issue() {
|
|
111
|
+
# URL-encode the gate name for the query
|
|
112
|
+
local query
|
|
113
|
+
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
|
|
114
|
+
|
|
115
|
+
local response
|
|
116
|
+
response=$(curl -sf \
|
|
117
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
118
|
+
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
|
|
119
|
+
2>/dev/null || echo "[]")
|
|
120
|
+
|
|
121
|
+
# Extract the first matching issue number
|
|
122
|
+
echo "$response" \
|
|
123
|
+
| grep -oP '"number":\s*\K[0-9]+' \
|
|
124
|
+
| head -1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# ── Build issue body ────────────────────────────────────────────────────────
|
|
128
|
+
build_body() {
|
|
129
|
+
local severity_badge
|
|
130
|
+
if [[ "$SEVERITY" == "error" ]]; then
|
|
131
|
+
severity_badge="**Severity:** Error"
|
|
132
|
+
else
|
|
133
|
+
severity_badge="**Severity:** Warning"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
cat <<BODY
|
|
137
|
+
## CI Gate Failure: ${GATE}
|
|
138
|
+
|
|
139
|
+
${severity_badge}
|
|
140
|
+
**Workflow:** ${WORKFLOW:-unknown}
|
|
141
|
+
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
|
142
|
+
**Commit:** \`${GITHUB_SHA:0:8}\`
|
|
143
|
+
**Run:** [View CI run](${RUN_URL})
|
|
144
|
+
|
|
145
|
+
### Details
|
|
146
|
+
|
|
147
|
+
${DETAILS}
|
|
148
|
+
|
|
149
|
+
### Resolution
|
|
150
|
+
|
|
151
|
+
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
*Auto-created by [ci-issue-reporter](${GITEA_URL}/${REPO}/src/branch/main/automation/ci-issue-reporter.sh)*
|
|
155
|
+
BODY
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# ── Build comment body (for existing issues) ────────────────────────────────
|
|
159
|
+
build_comment() {
|
|
160
|
+
cat <<COMMENT
|
|
161
|
+
### CI failure recurrence
|
|
162
|
+
|
|
163
|
+
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
|
164
|
+
**Commit:** \`${GITHUB_SHA:0:8}\`
|
|
165
|
+
**Run:** [View CI run](${RUN_URL})
|
|
166
|
+
|
|
167
|
+
${DETAILS}
|
|
168
|
+
COMMENT
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# ── Main ────────────────────────────────────────────────────────────────────
|
|
172
|
+
ensure_label
|
|
173
|
+
|
|
174
|
+
EXISTING=$(find_existing_issue)
|
|
175
|
+
|
|
176
|
+
if [[ -n "$EXISTING" ]]; then
|
|
177
|
+
# Append comment to existing issue
|
|
178
|
+
COMMENT_BODY=$(build_comment)
|
|
179
|
+
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
|
|
180
|
+
import sys, json
|
|
181
|
+
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
|
|
182
|
+
|
|
183
|
+
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
|
184
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
185
|
+
-H "Content-Type: application/json" \
|
|
186
|
+
"${API}/issues/${EXISTING}/comments" \
|
|
187
|
+
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
|
|
188
|
+
|
|
189
|
+
if [[ "$HTTP" == "201" ]]; then
|
|
190
|
+
echo "Commented on existing issue #${EXISTING}"
|
|
191
|
+
else
|
|
192
|
+
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
|
|
193
|
+
fi
|
|
194
|
+
else
|
|
195
|
+
# Create new issue
|
|
196
|
+
ISSUE_BODY=$(build_body)
|
|
197
|
+
ISSUE_JSON=$(python3 -c "
|
|
198
|
+
import sys, json
|
|
199
|
+
body = sys.stdin.read()
|
|
200
|
+
print(json.dumps({
|
|
201
|
+
'title': sys.argv[1],
|
|
202
|
+
'body': body,
|
|
203
|
+
'labels': []
|
|
204
|
+
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
|
|
205
|
+
|
|
206
|
+
# Create the issue
|
|
207
|
+
RESPONSE=$(curl -sf -X POST \
|
|
208
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
209
|
+
-H "Content-Type: application/json" \
|
|
210
|
+
"${API}/issues" \
|
|
211
|
+
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
|
|
212
|
+
|
|
213
|
+
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
|
|
214
|
+
|
|
215
|
+
if [[ -n "$ISSUE_NUM" ]]; then
|
|
216
|
+
# Apply label (separate call — more reliable across Gitea versions)
|
|
217
|
+
LABEL_ID=$(curl -sf \
|
|
218
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
219
|
+
"${API}/labels" 2>/dev/null \
|
|
220
|
+
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
|
|
221
|
+
| head -1 || true)
|
|
222
|
+
|
|
223
|
+
if [[ -n "$LABEL_ID" ]]; then
|
|
224
|
+
curl -sf -X POST \
|
|
225
|
+
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
226
|
+
-H "Content-Type: application/json" \
|
|
227
|
+
"${API}/issues/${ISSUE_NUM}/labels" \
|
|
228
|
+
-d "{\"labels\":[${LABEL_ID}]}" \
|
|
229
|
+
> /dev/null 2>&1 || true
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
|
|
233
|
+
else
|
|
234
|
+
echo "WARNING: Failed to create issue"
|
|
235
|
+
echo "Response: ${RESPONSE}"
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"defaultConnection": "moko",
|
|
3
|
+
"connections": {
|
|
4
|
+
"moko": {
|
|
5
|
+
"baseUrl": "https://git.mokoconsulting.tech",
|
|
6
|
+
"token": "your-gitea-access-token"
|
|
7
|
+
},
|
|
8
|
+
"github-mirror": {
|
|
9
|
+
"baseUrl": "https://gitea.example.com",
|
|
10
|
+
"token": "your-other-token"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GiteaConnection, ApiResponse } from './types.js';
|
|
2
|
+
export declare class GiteaClient {
|
|
3
|
+
private readonly base_url;
|
|
4
|
+
private readonly headers;
|
|
5
|
+
private readonly insecure;
|
|
6
|
+
constructor(conn: GiteaConnection);
|
|
7
|
+
get(endpoint: string, params?: Record<string, string>): Promise<ApiResponse>;
|
|
8
|
+
post(endpoint: string, body?: unknown): Promise<ApiResponse>;
|
|
9
|
+
patch(endpoint: string, body: unknown): Promise<ApiResponse>;
|
|
10
|
+
put(endpoint: string, body: unknown): Promise<ApiResponse>;
|
|
11
|
+
delete(endpoint: string, body?: unknown): Promise<ApiResponse>;
|
|
12
|
+
private buildUrl;
|
|
13
|
+
private request;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
*
|
|
3
|
+
* This file is part of a Moko Consulting project.
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
*
|
|
7
|
+
* FILE INFORMATION
|
|
8
|
+
* DEFGROUP: gitea-api-mcp.Client
|
|
9
|
+
* INGROUP: gitea-api-mcp
|
|
10
|
+
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
|
|
11
|
+
* PATH: /src/client.ts
|
|
12
|
+
* VERSION: 01.00.00
|
|
13
|
+
* BRIEF: HTTP client for Gitea REST API v1
|
|
14
|
+
*/
|
|
15
|
+
import * as https from 'node:https';
|
|
16
|
+
import * as http from 'node:http';
|
|
17
|
+
const API_PREFIX = '/api/v1';
|
|
18
|
+
const TIMEOUT_MS = 30_000;
|
|
19
|
+
export class GiteaClient {
|
|
20
|
+
base_url;
|
|
21
|
+
headers;
|
|
22
|
+
insecure;
|
|
23
|
+
constructor(conn) {
|
|
24
|
+
this.base_url = conn.baseUrl.replace(/\/+$/, '') + API_PREFIX;
|
|
25
|
+
this.headers = {
|
|
26
|
+
'Authorization': `token ${conn.token}`,
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'Accept': 'application/json',
|
|
29
|
+
};
|
|
30
|
+
this.insecure = conn.insecure ?? false;
|
|
31
|
+
}
|
|
32
|
+
async get(endpoint, params) {
|
|
33
|
+
return this.request(this.buildUrl(endpoint, params), 'GET');
|
|
34
|
+
}
|
|
35
|
+
async post(endpoint, body) {
|
|
36
|
+
return this.request(this.buildUrl(endpoint), 'POST', body);
|
|
37
|
+
}
|
|
38
|
+
async patch(endpoint, body) {
|
|
39
|
+
return this.request(this.buildUrl(endpoint), 'PATCH', body);
|
|
40
|
+
}
|
|
41
|
+
async put(endpoint, body) {
|
|
42
|
+
return this.request(this.buildUrl(endpoint), 'PUT', body);
|
|
43
|
+
}
|
|
44
|
+
async delete(endpoint, body) {
|
|
45
|
+
return this.request(this.buildUrl(endpoint), 'DELETE', body);
|
|
46
|
+
}
|
|
47
|
+
buildUrl(endpoint, params) {
|
|
48
|
+
const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
49
|
+
const url = new URL(`${this.base_url}${path}`);
|
|
50
|
+
if (params) {
|
|
51
|
+
for (const [key, value] of Object.entries(params)) {
|
|
52
|
+
url.searchParams.set(key, value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return url.toString();
|
|
56
|
+
}
|
|
57
|
+
request(url, method, body) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const parsed = new URL(url);
|
|
60
|
+
const is_https = parsed.protocol === 'https:';
|
|
61
|
+
const transport = is_https ? https : http;
|
|
62
|
+
const options = {
|
|
63
|
+
hostname: parsed.hostname,
|
|
64
|
+
port: parsed.port || (is_https ? 443 : 80),
|
|
65
|
+
path: parsed.pathname + parsed.search,
|
|
66
|
+
method,
|
|
67
|
+
headers: { ...this.headers },
|
|
68
|
+
timeout: TIMEOUT_MS,
|
|
69
|
+
};
|
|
70
|
+
if (this.insecure && is_https) {
|
|
71
|
+
options.rejectUnauthorized = false;
|
|
72
|
+
}
|
|
73
|
+
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
74
|
+
if (payload) {
|
|
75
|
+
options.headers['Content-Length'] = Buffer.byteLength(payload).toString();
|
|
76
|
+
}
|
|
77
|
+
const req = transport.request(options, (res) => {
|
|
78
|
+
const chunks = [];
|
|
79
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
80
|
+
res.on('end', () => {
|
|
81
|
+
const raw = Buffer.concat(chunks).toString('utf-8');
|
|
82
|
+
let data;
|
|
83
|
+
try {
|
|
84
|
+
data = JSON.parse(raw);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
data = raw;
|
|
88
|
+
}
|
|
89
|
+
resolve({ status: res.statusCode ?? 0, data });
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
req.on('error', (err) => reject(err));
|
|
93
|
+
req.on('timeout', () => {
|
|
94
|
+
req.destroy();
|
|
95
|
+
reject(new Error('Request timed out'));
|
|
96
|
+
});
|
|
97
|
+
if (payload) {
|
|
98
|
+
req.write(payload);
|
|
99
|
+
}
|
|
100
|
+
req.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=client.js.map
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
*
|
|
3
|
+
* This file is part of a Moko Consulting project.
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
*
|
|
7
|
+
* FILE INFORMATION
|
|
8
|
+
* DEFGROUP: gitea-api-mcp.Config
|
|
9
|
+
* INGROUP: gitea-api-mcp
|
|
10
|
+
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
|
|
11
|
+
* PATH: /src/config.ts
|
|
12
|
+
* VERSION: 01.00.00
|
|
13
|
+
* BRIEF: Configuration loader for Gitea API MCP connections
|
|
14
|
+
*/
|
|
15
|
+
import { readFile } from 'node:fs/promises';
|
|
16
|
+
import { resolve } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
const CONFIG_FILENAME = '.mcp_mokogitea.json';
|
|
19
|
+
export async function loadConfig() {
|
|
20
|
+
const config_path = process.env.GITEA_API_MCP_CONFIG
|
|
21
|
+
? resolve(process.env.GITEA_API_MCP_CONFIG)
|
|
22
|
+
: resolve(homedir(), CONFIG_FILENAME);
|
|
23
|
+
try {
|
|
24
|
+
const raw = await readFile(config_path, 'utf-8');
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (!parsed.connections || Object.keys(parsed.connections).length === 0) {
|
|
27
|
+
throw new Error('No connections defined in config');
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
connections: parsed.connections,
|
|
31
|
+
defaultConnection: parsed.defaultConnection ?? Object.keys(parsed.connections)[0],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
36
|
+
throw new Error(`Failed to load config from ${config_path}: ${message}\n` +
|
|
37
|
+
`Create ${config_path} — see config.example.json for format.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function getConnection(config, name) {
|
|
41
|
+
const key = name ?? config.defaultConnection;
|
|
42
|
+
const conn = config.connections[key];
|
|
43
|
+
if (!conn) {
|
|
44
|
+
throw new Error(`Connection "${key}" not found. Available: ${Object.keys(config.connections).join(', ')}`);
|
|
45
|
+
}
|
|
46
|
+
return conn;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=config.js.map
|
package/dist/index.d.ts
ADDED