Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c06a23f4f8 | ||
|
|
568c283aff | ||
|
|
87f9150806 | ||
|
|
14fd6ee5c5 | ||
|
|
b9328d4161 | ||
|
|
d8ae7ee6f6 | ||
|
|
2ae60299b7 | ||
|
|
c6b172dce7 | ||
|
|
d3cfef4147 | ||
|
|
7abf9f7734 | ||
|
|
45dd78ec79 | ||
|
|
29f3c1a327 | ||
|
|
a5232f8f6d | ||
|
|
6798e0fbf5 | ||
|
|
9436945487 | ||
|
|
71cf9ac60d | ||
|
|
0219bcdd86 |
@@ -51,7 +51,7 @@ def seed_base_models():
|
||||
pid = 'provider.openai' if m['provider']=='openai' else 'provider.one-api'
|
||||
exec_sql('''INSERT OR IGNORE INTO llm_models(id,provider_id,owner_username,model_name,display_name,context_window,input_modalities,output_modalities,supports_tools,supports_vision,supports_reasoning,cost_tier,is_base,scope,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', (m['id'], pid, 'system', m['model'], m['display'], m['context'], '["text"]', '["text"]', m['tools'], m['vision'], m['reasoning'], m['tier'], 1, 'global', 'active', now(), now()))
|
||||
if not one('SELECT * FROM gitops_baseline WHERE id=?', ('current',)):
|
||||
exec_sql('INSERT INTO gitops_baseline(id,active_version,active_tag,source_package,entry,update_mode,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?)', ('current','V1R1C0','V1R1C0','AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706',ENTRY,'git_patch_only','active',now(),now()))
|
||||
exec_sql('INSERT INTO gitops_baseline(id,active_version,active_tag,source_package,entry,update_mode,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?)', ('current','V1R1C0','V1R1C0-baseline-20260706','AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip',ENTRY,'git_patch_only','active',now(),now()))
|
||||
if not one('SELECT * FROM gitops_versions WHERE version=?', ('V1R1C0',)):
|
||||
exec_sql('INSERT INTO gitops_versions(version,tag,level,summary,status,created_by,created_at) VALUES(?,?,?,?,?,?,?)', ('V1R1C0','V1R1C0','R','MultiUser runtime selector + GitOps + custom LLM priority registry','ready','system',now()))
|
||||
|
||||
@@ -127,6 +127,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
body = '<h2>Base Models</h2>'+self.table([m for m in model_visible_to(username) if int(m.get('is_base') or 0)==1]); body += '<h2>Custom Models</h2>'+self.table([m for m in model_visible_to(username) if int(m.get('is_base') or 0)==0]); body += '<h2>Priority</h2>'+self.table(priority_for(username)); self.send_html('Model Registry', body); return
|
||||
if path in ('/api/runtime/status','/run'):
|
||||
data = rows('SELECT * FROM runtime_instances ORDER BY created_at DESC LIMIT 100'); self.send_html('Runtime', self.table(data)) if path == '/run' else self.send_json(data); return
|
||||
if path in ('/hook','/api/hook'):
|
||||
self.send_html('Git Webhook', '<pre>'+esc(json.dumps({'status':'ready','endpoint':'/hook/git','version':'V1R1C0'}, ensure_ascii=False, indent=2))+'</pre>'); return
|
||||
if path == '/api/audit': self.send_json(rows('SELECT * FROM model_audit_logs ORDER BY created_at DESC LIMIT 200')); return
|
||||
self.send_json({'error':'not_found','path':path},404)
|
||||
except Exception as e: self.send_json({'error':str(e)},500)
|
||||
@@ -236,6 +238,22 @@ class Handler(BaseHTTPRequestHandler):
|
||||
elif '模型' in text and ('添加' in text or '新增' in text or 'add' in text.lower()): intent='add_custom_model'; plan=['validate provider','store api key as secret_ref','call /api/model/custom/add']
|
||||
elif 'patch' in text.lower() or '补丁' in text: intent='create_patch'; plan=['create patch branch','commit patch metadata','run tests']
|
||||
self.send_json({'intent':intent,'action_plan':plan,'execute':False,'safety_check':'V-level changes require owner approval'}); return
|
||||
if path in ('/hook/git','/api/hook/git'):
|
||||
event_id = uid('hook')
|
||||
event_file = ROOT / 'artifacts' / 'git-webhook-events.jsonl'
|
||||
event_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
event = {
|
||||
'id': event_id,
|
||||
'received_at': now(),
|
||||
'event': self.headers.get('X-Gitea-Event') or self.headers.get('X-GitHub-Event') or data.get('event') or 'unknown',
|
||||
'ref': data.get('ref'),
|
||||
'repository': (data.get('repository') or {}).get('full_name') if isinstance(data.get('repository'), dict) else data.get('repository'),
|
||||
}
|
||||
with event_file.open('a', encoding='utf-8') as fh:
|
||||
fh.write(json.dumps(event, ensure_ascii=False) + '\n')
|
||||
smoke = subprocess.run(['bash','scripts/package_contract_check_v1r1c0.sh'], cwd=str(WORKSPACE), text=True, capture_output=True, timeout=120)
|
||||
audit('webhook','git_event',event_id,{'event':event,'smoke_code':smoke.returncode})
|
||||
self.send_json({'status':'accepted' if smoke.returncode == 0 else 'smoke_failed','id':event_id,'event':event,'smoke':{'code':smoke.returncode,'stdout':smoke.stdout[-2000:],'stderr':smoke.stderr[-2000:]}}); return
|
||||
self.send_json({'error':'not_found','path':path},404)
|
||||
except Exception as e: self.send_json({'error':str(e)},500)
|
||||
|
||||
|
||||
+22
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"active_version": "V1R1C0",
|
||||
"active_tag": "V1R1C0-baseline-20260706",
|
||||
"mode": "gitops-online-baseline",
|
||||
"mode": "gitops-online",
|
||||
"version_policy": "VxRxCx",
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_deprecated": true,
|
||||
@@ -24,5 +24,25 @@
|
||||
"user_model_priority": true,
|
||||
"chatgpt_rw": true,
|
||||
"codex_rw": true
|
||||
}
|
||||
},
|
||||
"version": "V1R1C0",
|
||||
"baseline_tag": "V1R1C0-baseline-20260706",
|
||||
"source": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
||||
"source_package": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
||||
"zip_update_mode": "deprecated",
|
||||
"chatgpt_access": true,
|
||||
"codex_access": true,
|
||||
"git_url": "https://cnnetmkt.top/git/aios/aios-v23.git",
|
||||
"short_links": [
|
||||
"/git",
|
||||
"/base",
|
||||
"/ver",
|
||||
"/diff",
|
||||
"/patch",
|
||||
"/model",
|
||||
"/run",
|
||||
"/api",
|
||||
"/hook"
|
||||
],
|
||||
"updated_at": "2026-07-06T07:16:01.842497+00:00"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=AIOS GitOps Gitea
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aiman
|
||||
Group=aiman
|
||||
WorkingDirectory=/data/aios-gitops/runtime/gitea-bin
|
||||
ExecStart=/data/aios-gitops/runtime/bin/gitea web --config /data/aios-gitops/runtime/gitea-bin/custom/conf/app.ini
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
Environment=USER=aiman
|
||||
Environment=HOME=/home/aiman
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,38 @@
|
||||
APP_NAME = AIOS GitOps
|
||||
RUN_USER = aiman
|
||||
WORK_PATH = /data/aios-gitops/runtime/gitea-bin
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
HTTP_ADDR = 127.0.0.1
|
||||
HTTP_PORT = 3000
|
||||
DOMAIN = cnnetmkt.top
|
||||
ROOT_URL = https://cnnetmkt.top/git/
|
||||
SERVE_FROM_SUB_PATH = true
|
||||
DISABLE_SSH = true
|
||||
OFFLINE_MODE = false
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = /data/aios-gitops/runtime/gitea-bin/data/gitea.db
|
||||
|
||||
[repository]
|
||||
ROOT = /data/aios-gitops/runtime/gitea-bin/data/repositories
|
||||
DEFAULT_BRANCH = main
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = __SECRET_KEY__
|
||||
INTERNAL_TOKEN = __INTERNAL_TOKEN__
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = true
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
|
||||
[webhook]
|
||||
ALLOWED_HOST_LIST = *
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = Info
|
||||
ROOT_PATH = /data/aios-gitops/runtime/gitea-bin/log
|
||||
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.22
|
||||
container_name: aios-gitea
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__server__DOMAIN=cnnetmkt.top
|
||||
- GITEA__server__ROOT_URL=https://cnnetmkt.top/git/
|
||||
- GITEA__server__SERVE_FROM_SUB_PATH=true
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
- GITEA__server__SSH_DOMAIN=cnnetmkt.top
|
||||
- GITEA__server__SSH_PORT=2222
|
||||
- GITEA__service__DISABLE_REGISTRATION=true
|
||||
- GITEA__webhook__ALLOWED_HOST_LIST=*
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
volumes:
|
||||
- /data/aios-gitops/runtime/gitea:/data
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
@@ -0,0 +1,42 @@
|
||||
# BEGIN AIOS V1R1C0 GitOps short links
|
||||
location = /git { return 302 /git/; }
|
||||
location /git/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /git;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /api { return 302 /api/; }
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:18890/api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location = /base { proxy_pass http://127.0.0.1:18890/base; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /ver { proxy_pass http://127.0.0.1:18890/ver; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /diff { proxy_pass http://127.0.0.1:18890/diff; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /patch { proxy_pass http://127.0.0.1:18890/patch; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /model { proxy_pass http://127.0.0.1:18890/model; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /run { proxy_pass http://127.0.0.1:18890/run; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /hook { proxy_pass http://127.0.0.1:18890/hook; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location /hook/ {
|
||||
proxy_pass http://127.0.0.1:18890/hook/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
# END AIOS V1R1C0 GitOps short links
|
||||
@@ -0,0 +1,12 @@
|
||||
# cnnetmkt wildcard certificate
|
||||
|
||||
generated_at=2026-07-06T16:24:28+08:00
|
||||
issuer=Let's Encrypt via lego DNS-01 Huawei Cloud
|
||||
domains=cnnetmkt.top,*.cnnetmkt.top
|
||||
live_dir=/etc/letsencrypt/live/cnnetmkt-wildcard
|
||||
nginx_sites=/etc/nginx/sites-available/aios-user-portal.conf,/etc/nginx/sites-available/g-cnnetmkt-gitea.conf
|
||||
renew_service=cnnetmkt-wildcard-renew.service
|
||||
renew_timer=cnnetmkt-wildcard-renew.timer
|
||||
secret_env=/etc/letsencrypt/huaweicloud-cnnetmkt.env
|
||||
backup_dir=/data/aios-gitops/backups/nginx-wildcard-20260706-162202
|
||||
status=issued
|
||||
@@ -0,0 +1,52 @@
|
||||
# AIOS GitOps alias for https://g.cnnetmkt.top/
|
||||
# Note: this server block intentionally keeps /git/ as the Gitea app prefix,
|
||||
# matching the current Gitea ROOT_URL and avoiding a disruptive app.ini rewrite.
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name g.cnnetmkt.top;
|
||||
|
||||
access_log /var/log/nginx/aios-g-cnnetmkt.access.log;
|
||||
error_log /var/log/nginx/aios-g-cnnetmkt.error.log;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/cnnetmkt-wildcard/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/cnnetmkt-wildcard/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
location = / {
|
||||
return 302 /git/;
|
||||
}
|
||||
|
||||
location = /git {
|
||||
return 302 /git/;
|
||||
}
|
||||
|
||||
location /git/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /git;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name g.cnnetmkt.top;
|
||||
|
||||
location = / {
|
||||
return 302 https://$host/git/;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=AIOS V1R1C0 GitOps Model API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aiman
|
||||
Group=aiman
|
||||
Environment=AIOS_GITOPS_ROOT=/data/aios-gitops
|
||||
Environment=AIOS_GITOPS_WORKSPACE=/data/aios-gitops/workspace
|
||||
Environment=AIOS_GITOPS_DB=/data/aios-gitops/runtime/gitops_model.sqlite3
|
||||
Environment=AIOS_GITOPS_TOKEN_FILE=/data/aios-gitops/runtime/secrets/gitops.tokens
|
||||
Environment=AIOS_GITOPS_HOST=127.0.0.1
|
||||
Environment=AIOS_GITOPS_PORT=18890
|
||||
ExecStart=/usr/bin/python3 /data/aios-gitops/workspace/active-runtime/app/aios_gitops_model_api.py
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,83 @@
|
||||
# cnnetmkt SSL ingress status
|
||||
|
||||
generated_at=2026-07-06T15:48:12+08:00
|
||||
g_route_status=installed
|
||||
certbot_status=issued-g-user-portal
|
||||
apex_dns_status=blocked_until_cnnetmkt.top_has_A_record_to_1.95.164.183
|
||||
wildcard_status=blocked_until_dns01_provider_credentials_available
|
||||
backup_dir=/data/aios-gitops/backups/nginx-g-cnnetmkt-20260706-154743
|
||||
tracked_nginx=/data/aios-gitops/workspace/deploy/nginx/g-cnnetmkt-gitea-server.conf
|
||||
|
||||
## validation
|
||||
### g.cnnetmkt.top
|
||||
HTTP/1.1 302 Moved Temporarily
|
||||
Server: nginx/1.24.0 (Ubuntu)
|
||||
Date: Mon, 06 Jul 2026 07:48:12 GMT
|
||||
Content-Type: text/html
|
||||
Content-Length: 154
|
||||
Location: https://g.cnnetmkt.top/git/
|
||||
Connection: keep-alive
|
||||
|
||||
<title>302 Found
|
||||
|
||||
subject=CN = user.cnnetmkt.top
|
||||
issuer=C = US, O = Let's Encrypt, CN = YE2
|
||||
notBefore=Jul 6 06:49:39 2026 GMT
|
||||
notAfter=Oct 4 06:49:38 2026 GMT
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:g.cnnetmkt.top, DNS:portal.cnnetmkt.top, DNS:user.cnnetmkt.top
|
||||
|
||||
### cnnetmkt.top
|
||||
HTTP/1.1 302 Found
|
||||
Server: nginx/1.24.0 (Ubuntu)
|
||||
Date: Mon, 06 Jul 2026 07:48:12 GMT
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 0
|
||||
Connection: keep-alive
|
||||
Location: /portal/login/
|
||||
X-Request-ID: 7c649abe-a2de-4b3e-87fe-325f4297738f
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: same-origin
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
|
||||
subject=CN = user.cnnetmkt.top
|
||||
issuer=C = US, O = Let's Encrypt, CN = YE2
|
||||
notBefore=Jul 6 06:49:39 2026 GMT
|
||||
notAfter=Oct 4 06:49:38 2026 GMT
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:g.cnnetmkt.top, DNS:portal.cnnetmkt.top, DNS:user.cnnetmkt.top
|
||||
|
||||
### user.cnnetmkt.top
|
||||
HTTP/1.1 302 Found
|
||||
Server: nginx/1.24.0 (Ubuntu)
|
||||
Date: Mon, 06 Jul 2026 07:48:12 GMT
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 0
|
||||
Connection: keep-alive
|
||||
Location: /portal/login/
|
||||
X-Request-ID: e3d70907-1ab7-456c-a725-7cb60647514d
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: same-origin
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
|
||||
subject=CN = user.cnnetmkt.top
|
||||
issuer=C = US, O = Let's Encrypt, CN = YE2
|
||||
notBefore=Jul 6 06:49:39 2026 GMT
|
||||
notAfter=Oct 4 06:49:38 2026 GMT
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:g.cnnetmkt.top, DNS:portal.cnnetmkt.top, DNS:user.cnnetmkt.top
|
||||
|
||||
## certbot
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Found the following certs:
|
||||
Certificate Name: user.cnnetmkt.top
|
||||
Serial Number: 529feebc03b37f2866c25b1465f37e14565
|
||||
Key Type: ECDSA
|
||||
Domains: user.cnnetmkt.top g.cnnetmkt.top portal.cnnetmkt.top
|
||||
Expiry Date: 2026-10-04 06:49:38+00:00 (VALID: 89 days)
|
||||
Certificate Path: /etc/letsencrypt/live/user.cnnetmkt.top/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/user.cnnetmkt.top/privkey.pem
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -0,0 +1,28 @@
|
||||
# CNNETMKT wildcard certificate status
|
||||
|
||||
generated_at=2026-07-06T16:24:28+08:00
|
||||
status=issued
|
||||
staging_status=ok
|
||||
dns_provider=huaweicloud
|
||||
secret_storage=/etc/letsencrypt/huaweicloud-cnnetmkt.env
|
||||
certificate_live_dir=/etc/letsencrypt/live/cnnetmkt-wildcard
|
||||
nginx_status=loaded
|
||||
backup_dir=/data/aios-gitops/backups/nginx-wildcard-20260706-162202
|
||||
renew_timer=cnnetmkt-wildcard-renew.timer
|
||||
|
||||
## certificate
|
||||
|
||||
subject=CN = *.cnnetmkt.top
|
||||
issuer=C = US, O = Let's Encrypt, CN = YE2
|
||||
notBefore=Jul 6 07:25:55 2026 GMT
|
||||
notAfter=Oct 4 07:25:54 2026 GMT
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:*.cnnetmkt.top, DNS:cnnetmkt.top
|
||||
|
||||
## routes
|
||||
|
||||
- https://cnnetmkt.top/
|
||||
- https://cnnetmkt.top/git/
|
||||
- https://g.cnnetmkt.top/
|
||||
- https://g.cnnetmkt.top/git/
|
||||
- https://user.cnnetmkt.top/
|
||||
@@ -0,0 +1,48 @@
|
||||
# AIOS V1R1C0 Online Baseline Acceptance
|
||||
|
||||
AIOS_V1R1C0_ONLINE_BASELINE_STATUS=PARTIAL
|
||||
DOMAIN=https://cnnetmkt.top
|
||||
SHORT_LINKS=/git,/base,/ver,/diff,/patch,/model,/run,/api,/hook
|
||||
PROXY=NGINX_TEMP (NPM_NOT_RUNNING)
|
||||
GIT_URL=https://cnnetmkt.top/git/aios/aios-v23.git
|
||||
BASELINE_VERSION=V1R1C0
|
||||
BASELINE_TAG=V1R1C0-baseline-20260706
|
||||
WORKSPACE=/data/aios-gitops/workspace
|
||||
WEBOPS_REUSED=YES
|
||||
CASDOOR_REUSED=PARTIAL
|
||||
OPENCLAW_REUSED=PARTIAL
|
||||
HERMES_REUSED=YES
|
||||
ONE_API_REUSED=YES
|
||||
GITEA_REUSED=NEW
|
||||
CUSTOM_LLM_STATUS=OK
|
||||
MODEL_PRIORITY_STATUS=OK
|
||||
RUNTIME_SELECTOR_STATUS=OK
|
||||
ACCEPTANCE_REPORT=/data/aios-gitops/artifacts/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md
|
||||
KNOWN_ISSUES=NPM is not running on this host, so system nginx currently serves the short links; OpenClaw gateway is active but native openclaw agent execution still needs gateway/provider credentials; host-side DNS resolution for cnnetmkt.top is unreliable, so local validation used --resolve to 127.0.0.1; Docker Hub pull for Gitea was blocked by the existing Docker proxy path and was replaced with official Gitea binary deployment.
|
||||
NEXT_UPDATE_MODE=git_patch_only
|
||||
ZIP_UPDATE_MODE=deprecated
|
||||
|
||||
## Evidence
|
||||
|
||||
- Imported package: /data/aios-gitops/imports/AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip
|
||||
- Package SHA256: dbee5d45aab02aab33e862bb9beb6115b3b43137e0fb5e00c98448ac1ecfd0f9
|
||||
- Git workspace: /data/aios-gitops/workspace
|
||||
- Baseline branch: main
|
||||
- Baseline tag: V1R1C0-baseline-20260706
|
||||
- Git remote: https://cnnetmkt.top/git/aios/aios-v23.git
|
||||
- Gitea internal version: 1.22.6
|
||||
- API service: aios-gitops-model-api.service on 127.0.0.1:18890
|
||||
- Gitea service: aios-gitea.service on 127.0.0.1:3000
|
||||
- Hermes executor health: ok
|
||||
- One API reused at: 127.0.0.1:13000
|
||||
- WebOps reused container: aios-webops-server-hw-webops-1
|
||||
- Parallel server agents: git_service_agent, entry_proxy_agent, gitops_api_uat_agent, acceptance_report_agent
|
||||
- Parallel agent report: /data/aios-gitops/artifacts/AIOS_V1R1C0_PARALLEL_AGENT_STATUS.json
|
||||
- Final evidence log: /data/aios-gitops/artifacts/AIOS_V1R1C0_FINAL_EVIDENCE.txt
|
||||
|
||||
## GitOps Rules
|
||||
|
||||
- ZIP update mode is deprecated after initial import.
|
||||
- Future updates must be git branch + commit + push + webhook.
|
||||
- Patches are represented as git branches and are reviewable by diff.
|
||||
- ChatGPT/Codex write path is /api/git-control-compatible endpoints exposed by the GitOps API.
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
ZIP_NAME="AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip"
|
||||
ZIP_PATH="$ROOT/imports/$ZIP_NAME"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
LOG_DIR="$ROOT/logs"
|
||||
ARTIFACT_DIR="$ROOT/artifacts"
|
||||
PRECHECK="$LOG_DIR/preflight-v1r1c0.md"
|
||||
RUN_LOG="$LOG_DIR/gitops-v1r1c0-migration.log"
|
||||
FAILED_REPORT="$ARTIFACT_DIR/AIOS_V1R1C0_ONLINE_BASELINE_FAILED.md"
|
||||
ACCEPT_REPORT="$ARTIFACT_DIR/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md"
|
||||
BASELINE_TAG="V1R1C0-baseline-20260706"
|
||||
|
||||
mkdir -p "$ROOT"/{imports,workspace,repos,scripts,logs,backups,runtime,artifacts,adapters}
|
||||
touch "$RUN_LOG"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date -Is)" "$*" | tee -a "$RUN_LOG"
|
||||
}
|
||||
|
||||
run_section() {
|
||||
local title="$1"
|
||||
shift
|
||||
{
|
||||
printf '\n## %s\n\n' "$title"
|
||||
printf '```text\n'
|
||||
"$@" 2>&1 || true
|
||||
printf '```\n'
|
||||
} >> "$PRECHECK"
|
||||
}
|
||||
|
||||
write_failed_report() {
|
||||
local step="$1"
|
||||
local message="$2"
|
||||
cat > "$FAILED_REPORT" <<EOF
|
||||
# AIOS V1R1C0 Online Baseline Failed
|
||||
|
||||
- status: FAILED
|
||||
- failed_step: $step
|
||||
- error: $message
|
||||
- package: $ZIP_PATH
|
||||
- workspace: $WORKSPACE
|
||||
- production_impact: none-intended
|
||||
- npm_modified: NO
|
||||
- webops_modified: NO
|
||||
- git_modified: PARTIAL
|
||||
- openclaw_modified: NO
|
||||
- generated_at: $(date -Is)
|
||||
|
||||
## Completed Actions
|
||||
|
||||
- Created /data/aios-gitops directory structure.
|
||||
- Generated preflight report when possible.
|
||||
- Preserved original ZIP if present.
|
||||
- Stopped before production routing changes.
|
||||
|
||||
## Rollback Advice
|
||||
|
||||
The active production stack was not switched by this script. Keep the existing
|
||||
WebOps/NPM/Casdoor/OpenClaw/Hermes services unchanged. Remove only the new
|
||||
/data/aios-gitops workspace if an operator explicitly wants to discard this
|
||||
failed import attempt.
|
||||
|
||||
## Next Step
|
||||
|
||||
Fix the failed step, then rerun the migration script. Do not treat this state
|
||||
as an online baseline.
|
||||
EOF
|
||||
}
|
||||
|
||||
preflight() {
|
||||
log "preflight start"
|
||||
cat > "$PRECHECK" <<EOF
|
||||
# AIOS V1R1C0 GitOps Preflight
|
||||
|
||||
- generated_at: $(date -Is)
|
||||
- hostname: $(hostname)
|
||||
- user: $(whoami)
|
||||
- root: $ROOT
|
||||
- package: $ZIP_PATH
|
||||
|
||||
## Reuse Summary
|
||||
|
||||
- NPM_REUSED: UNKNOWN
|
||||
- WEBOPS_REUSED: UNKNOWN
|
||||
- CASDOOR_REUSED: UNKNOWN
|
||||
- OPENCLAW_REUSED: UNKNOWN
|
||||
- HERMES_REUSED: UNKNOWN
|
||||
- ONE_API_REUSED: UNKNOWN
|
||||
- GITEA_REUSED: UNKNOWN
|
||||
|
||||
EOF
|
||||
run_section "hostname" hostname
|
||||
run_section "date" date -Is
|
||||
run_section "whoami" whoami
|
||||
run_section "pwd" pwd
|
||||
run_section "df -h" df -h
|
||||
run_section "free -h" free -h
|
||||
run_section "docker ps" docker ps
|
||||
run_section "docker compose version" bash -lc 'docker compose version || docker-compose version || true'
|
||||
run_section "ss -lntp" ss -lntp
|
||||
run_section "ls /data" ls -lah /data
|
||||
run_section "ls /opt" ls -lah /opt
|
||||
run_section "component containers" bash -lc "docker ps | grep -Ei 'npm|nginx-proxy-manager|gitea|casdoor|openclaw|hermes|webops|one-api|postgres|redis|nats' || true"
|
||||
run_section "component paths" bash -lc "find /data /opt -maxdepth 4 \\( -iname '*npm*' -o -iname '*gitea*' -o -iname '*openclaw*' -o -iname '*webops*' -o -iname '*casdoor*' -o -iname '*one-api*' \\) 2>/dev/null || true"
|
||||
run_section "component services" bash -lc "systemctl list-units --type=service --no-pager | grep -Ei 'openclaw|hermes|aios|webops|npm|gitea|casdoor' || true"
|
||||
log "preflight done: $PRECHECK"
|
||||
}
|
||||
|
||||
backup_state() {
|
||||
log "backup start"
|
||||
local backup_dir="$ROOT/backups/pre-v1r1c0-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$backup_dir"
|
||||
cp -a /data/agent "$backup_dir/data-agent" 2>/dev/null || true
|
||||
cp -a /data/aios "$backup_dir/data-aios" 2>/dev/null || true
|
||||
cp -a /opt/aios "$backup_dir/opt-aios" 2>/dev/null || true
|
||||
cp -a /etc/systemd/system "$backup_dir/systemd-system" 2>/dev/null || true
|
||||
docker ps > "$backup_dir/docker-ps.txt" 2>/dev/null || true
|
||||
ss -lntp > "$backup_dir/ports.txt" 2>/dev/null || true
|
||||
find /data /opt -maxdepth 4 \( -iname '*npm*' -o -iname '*nginx-proxy-manager*' \) 2>/dev/null > "$backup_dir/npm-paths.txt" || true
|
||||
while IFS= read -r p; do
|
||||
[ -e "$p" ] || continue
|
||||
safe_name="$(printf '%s' "$p" | sed 's#^/##; s#[^A-Za-z0-9._-]#_#g')"
|
||||
cp -a "$p" "$backup_dir/$safe_name" 2>/dev/null || true
|
||||
done < "$backup_dir/npm-paths.txt"
|
||||
printf '%s\n' "$backup_dir" > "$ROOT/runtime/latest-backup-dir.txt"
|
||||
log "backup done: $backup_dir"
|
||||
}
|
||||
|
||||
import_zip() {
|
||||
log "import start"
|
||||
if [ ! -f "$ZIP_PATH" ]; then
|
||||
write_failed_report "locate_zip" "BLOCKED: missing $ZIP_NAME"
|
||||
log "BLOCKED: missing $ZIP_NAME"
|
||||
exit 2
|
||||
fi
|
||||
mkdir -p "$WORKSPACE"
|
||||
resolved="$(readlink -f "$WORKSPACE")"
|
||||
if [ "$resolved" != "$WORKSPACE" ]; then
|
||||
write_failed_report "workspace_resolve" "Unexpected workspace path: $resolved"
|
||||
exit 3
|
||||
fi
|
||||
find "$WORKSPACE" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
|
||||
unzip -q -o "$ZIP_PATH" -d "$WORKSPACE"
|
||||
shopt -s nullglob dotglob
|
||||
entries=("$WORKSPACE"/*)
|
||||
if [ "${#entries[@]}" -eq 1 ] && [ -d "${entries[0]}" ]; then
|
||||
tmp_dir="$WORKSPACE/.flatten-tmp"
|
||||
mv "${entries[0]}" "$tmp_dir"
|
||||
shopt -s dotglob
|
||||
mv "$tmp_dir"/* "$WORKSPACE"/
|
||||
rmdir "$tmp_dir"
|
||||
fi
|
||||
chmod +x "$WORKSPACE"/package_verify_full.sh 2>/dev/null || true
|
||||
chmod +x "$WORKSPACE"/scripts/*.sh "$WORKSPACE"/tests/*.sh 2>/dev/null || true
|
||||
log "import done"
|
||||
}
|
||||
|
||||
run_package_checks() {
|
||||
log "package checks start"
|
||||
cd "$WORKSPACE"
|
||||
if ! bash scripts/package_contract_check_v1r1c0.sh > "$LOG_DIR/package_contract_check_v1r1c0.log" 2>&1; then
|
||||
write_failed_report "package_contract_check_v1r1c0" "scripts/package_contract_check_v1r1c0.sh failed; see $LOG_DIR/package_contract_check_v1r1c0.log"
|
||||
exit 4
|
||||
fi
|
||||
if ! bash tests/run_v1r1c0_acceptance.sh > "$LOG_DIR/run_v1r1c0_acceptance.log" 2>&1; then
|
||||
write_failed_report "run_v1r1c0_acceptance" "tests/run_v1r1c0_acceptance.sh failed; see $LOG_DIR/run_v1r1c0_acceptance.log"
|
||||
exit 5
|
||||
fi
|
||||
if ! ./package_verify_full.sh > "$LOG_DIR/package_verify_full.log" 2>&1; then
|
||||
write_failed_report "package_verify_full" "package_verify_full.sh failed; see $LOG_DIR/package_verify_full.log"
|
||||
exit 6
|
||||
fi
|
||||
log "package checks done"
|
||||
}
|
||||
|
||||
write_baseline_files() {
|
||||
log "write baseline files"
|
||||
cd "$WORKSPACE"
|
||||
mkdir -p baseline versions patches architecture evolution deploy ops tests docs/gitops docs/release
|
||||
cat > baseline/current.json <<'EOF'
|
||||
{
|
||||
"active_version": "V1R1C0",
|
||||
"active_tag": "V1R1C0-baseline-20260706",
|
||||
"mode": "gitops-online-baseline",
|
||||
"version_policy": "VxRxCx",
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_deprecated": true,
|
||||
"entry": "https://cnnetmkt.top",
|
||||
"links": {
|
||||
"git": "/git",
|
||||
"baseline": "/base",
|
||||
"versions": "/ver",
|
||||
"diff": "/diff",
|
||||
"patch": "/patch",
|
||||
"model": "/model",
|
||||
"runtime": "/run",
|
||||
"api": "/api",
|
||||
"hook": "/hook"
|
||||
},
|
||||
"features": {
|
||||
"multiuser_runtime": true,
|
||||
"shared_dedicated_runtime": true,
|
||||
"custom_llm": true,
|
||||
"user_model_priority": true,
|
||||
"chatgpt_rw": true,
|
||||
"codex_rw": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
cat > versions/V1R1C0.json <<'EOF'
|
||||
{
|
||||
"version": "V1R1C0",
|
||||
"tag": "V1R1C0-baseline-20260706",
|
||||
"type": "baseline",
|
||||
"name": "MultiUser GitOps Custom LLM Baseline",
|
||||
"source": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_mode": "deprecated_after_import"
|
||||
}
|
||||
EOF
|
||||
cat > architecture/current.json <<'EOF'
|
||||
{
|
||||
"version": "V1R1C0",
|
||||
"architecture_version": "V1",
|
||||
"release_version": "R1",
|
||||
"change_version": "C0",
|
||||
"components": [
|
||||
"webops",
|
||||
"owner-admin-user",
|
||||
"npm",
|
||||
"casdoor",
|
||||
"openclaw",
|
||||
"hermes",
|
||||
"one-api",
|
||||
"gitops-control",
|
||||
"baseline-registry",
|
||||
"custom-llm-registry",
|
||||
"runtime-selector"
|
||||
],
|
||||
"entry": "https://cnnetmkt.top",
|
||||
"proxy": "npm",
|
||||
"runtime_sync": "openclaw"
|
||||
}
|
||||
EOF
|
||||
cat > docs/release/VxRxCx_RULES.md <<'EOF'
|
||||
# AIOS VxRxCx Versioning Rules
|
||||
|
||||
VxRxCx is the only supported online baseline version format.
|
||||
|
||||
- V: architecture version
|
||||
- R: release version
|
||||
- C: change version
|
||||
|
||||
V-level changes require owner/admin approval and blue-green or equivalent safe switching.
|
||||
R-level changes require canary or test-environment validation.
|
||||
C-level changes require smoke test and automatic rollback on failure.
|
||||
|
||||
Initial imported online baseline for this package: V1R1C0-baseline-20260706.
|
||||
EOF
|
||||
cat > docs/gitops/CHATGPT_CODEX_ACCESS.md <<'EOF'
|
||||
# ChatGPT / Codex GitOps Access
|
||||
|
||||
Entry: https://cnnetmkt.top
|
||||
|
||||
Short links: /git, /base, /ver, /diff, /patch, /model, /run, /api, /hook.
|
||||
|
||||
All writes must become Git commits. Fixes must use patch branches. Releases must use VxRxCx tags.
|
||||
|
||||
Forbidden:
|
||||
|
||||
- direct production edits without Git history
|
||||
- API keys in Git
|
||||
- API keys in plaintext logs
|
||||
- bypassing owner/admin approval for V-level changes
|
||||
EOF
|
||||
}
|
||||
|
||||
init_git() {
|
||||
log "git init start"
|
||||
cd "$WORKSPACE"
|
||||
git init
|
||||
git config user.name "AIOS GitOps Bot"
|
||||
git config user.email "gitops-bot@cnnetmkt.top"
|
||||
if [ ! -f .gitignore ]; then
|
||||
cat > .gitignore <<'EOF'
|
||||
.env
|
||||
*.secret
|
||||
*.key
|
||||
*.pem
|
||||
*.log
|
||||
__pycache__/
|
||||
node_modules/
|
||||
.venv/
|
||||
.DS_Store
|
||||
runtime/tmp/
|
||||
runtime/cache/
|
||||
logs/
|
||||
EOF
|
||||
fi
|
||||
git add .
|
||||
git commit -m "init(baseline): import AIOS V1R1C0 multiuser gitops custom llm baseline" || true
|
||||
git branch -M main
|
||||
git tag -a "$BASELINE_TAG" -m "AIOS V1R1C0 online baseline: multiuser gitops custom LLM" 2>/dev/null || true
|
||||
log "git init done"
|
||||
}
|
||||
|
||||
write_acceptance() {
|
||||
log "write acceptance report"
|
||||
cd "$WORKSPACE"
|
||||
cat > "$ACCEPT_REPORT" <<EOF
|
||||
# AIOS V1R1C0 Online Baseline Acceptance
|
||||
|
||||
1. server: $(hostname)
|
||||
2. zip_path: $ZIP_PATH
|
||||
3. npm_reused: PARTIAL_UNVERIFIED
|
||||
4. webops_reused: YES_DETECTED
|
||||
5. casdoor_reused: PARTIAL_UNVERIFIED
|
||||
6. openclaw_reused: YES_DETECTED
|
||||
7. hermes_reused: YES_DETECTED
|
||||
8. one_api_reused: YES_DETECTED
|
||||
9. git_service: LOCAL_GIT_READY_REMOTE_UI_PENDING
|
||||
10. git_url: PENDING_GITEA_OR_EXISTING_GIT_SERVICE
|
||||
11. short_links: /git,/base,/ver,/diff,/patch,/model,/run,/api,/hook
|
||||
12. baseline_version: V1R1C0
|
||||
13. baseline_tag: $BASELINE_TAG
|
||||
14. custom_llm_api: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
||||
15. model_priority: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
||||
16. runtime_selector: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
||||
17. gitops_patch: BASELINE_COMMIT_AND_TAG_READY
|
||||
18. smoke_test: PACKAGE_CHECKS_PASS
|
||||
19. known_issues:
|
||||
- NPM custom locations were not modified by this script.
|
||||
- Gitea/Web Git UI push was not completed by this script.
|
||||
- Spark sub-agent quota was unavailable during orchestration.
|
||||
20. next_update_mode: git_patch_only
|
||||
|
||||
## Git Evidence
|
||||
EOF
|
||||
{
|
||||
printf '\n### git status --short\n\n'
|
||||
git status --short
|
||||
printf '\n### git log --oneline -10\n\n'
|
||||
git log --oneline -10
|
||||
printf '\n### git tag grep V1R1C0\n\n'
|
||||
git tag | grep V1R1C0 || true
|
||||
} >> "$ACCEPT_REPORT"
|
||||
}
|
||||
|
||||
main() {
|
||||
preflight
|
||||
backup_state
|
||||
import_zip
|
||||
run_package_checks
|
||||
write_baseline_files
|
||||
init_git
|
||||
write_acceptance
|
||||
log "MIGRATION_LOCAL_BASELINE_OK"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
LOG="$ROOT/logs/runtime-services-v1r1c0.log"
|
||||
mkdir -p "$ROOT/logs" "$ROOT/runtime/secrets" "$WORKSPACE/deploy/systemd" "$WORKSPACE/deploy/gitea"
|
||||
touch "$LOG"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date -Is)" "$*" | tee -a "$LOG"
|
||||
}
|
||||
|
||||
cd "$WORKSPACE"
|
||||
|
||||
TOKEN_FILE="$ROOT/runtime/secrets/gitops.tokens"
|
||||
if [ ! -s "$TOKEN_FILE" ]; then
|
||||
(openssl rand -hex 32 || python3 -c 'import secrets; print(secrets.token_hex(32))') > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
fi
|
||||
|
||||
cat > deploy/systemd/aios-gitops-model-api.service <<EOF
|
||||
[Unit]
|
||||
Description=AIOS V1R1C0 GitOps Model API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aiman
|
||||
Group=aiman
|
||||
Environment=AIOS_GITOPS_ROOT=$ROOT
|
||||
Environment=AIOS_GITOPS_WORKSPACE=$WORKSPACE
|
||||
Environment=AIOS_GITOPS_DB=$ROOT/runtime/gitops_model.sqlite3
|
||||
Environment=AIOS_GITOPS_TOKEN_FILE=$TOKEN_FILE
|
||||
Environment=AIOS_GITOPS_HOST=127.0.0.1
|
||||
Environment=AIOS_GITOPS_PORT=18890
|
||||
ExecStart=/usr/bin/python3 $WORKSPACE/active-runtime/app/aios_gitops_model_api.py
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > deploy/gitea/docker-compose.yml <<'EOF'
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.22
|
||||
container_name: aios-gitea
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__server__DOMAIN=cnnetmkt.top
|
||||
- GITEA__server__ROOT_URL=https://cnnetmkt.top/git/
|
||||
- GITEA__server__SERVE_FROM_SUB_PATH=true
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
- GITEA__server__SSH_DOMAIN=cnnetmkt.top
|
||||
- GITEA__server__SSH_PORT=2222
|
||||
- GITEA__service__DISABLE_REGISTRATION=true
|
||||
- GITEA__webhook__ALLOWED_HOST_LIST=*
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
volumes:
|
||||
- /data/aios-gitops/runtime/gitea:/data
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
EOF
|
||||
|
||||
git add deploy/systemd/aios-gitops-model-api.service deploy/gitea/docker-compose.yml
|
||||
git commit -m "chore(gitops): add V1R1C0 runtime API and Gitea deployment manifests" || true
|
||||
|
||||
log "install API systemd unit"
|
||||
sudo cp deploy/systemd/aios-gitops-model-api.service /etc/systemd/system/aios-gitops-model-api.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now aios-gitops-model-api.service
|
||||
sleep 2
|
||||
systemctl is-active aios-gitops-model-api.service | tee -a "$LOG"
|
||||
curl -fsS http://127.0.0.1:18890/health | tee -a "$LOG"
|
||||
|
||||
log "start gitea"
|
||||
mkdir -p "$ROOT/adapters/gitea"
|
||||
cp deploy/gitea/docker-compose.yml "$ROOT/adapters/gitea/docker-compose.yml"
|
||||
(cd "$ROOT/adapters/gitea" && docker compose up -d)
|
||||
|
||||
for i in $(seq 1 60); do
|
||||
if curl -fsS http://127.0.0.1:3000/git/api/healthz >/dev/null 2>&1 || curl -fsS http://127.0.0.1:3000/git/ >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
curl -fsS http://127.0.0.1:3000/git/ >/dev/null || true
|
||||
docker ps --format '{{.Names}} {{.Image}} {{.Status}} {{.Ports}}' | grep -E '^aios-gitea ' | tee -a "$LOG" || true
|
||||
|
||||
log "runtime manifests committed and services attempted"
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
|
||||
cd "$WORKSPACE"
|
||||
git add active-runtime/app/aios_gitops_model_api.py scripts/hermes_gitops_parallel_agents.py
|
||||
git commit -m "fix(gitops): align baseline tag and secret scan" || true
|
||||
|
||||
python3 - <<'PY'
|
||||
import datetime
|
||||
import sqlite3
|
||||
|
||||
p = "/data/aios-gitops/runtime/gitops_model.sqlite3"
|
||||
con = sqlite3.connect(p)
|
||||
con.execute(
|
||||
"UPDATE gitops_baseline SET active_version=?, active_tag=?, source_package=?, update_mode=?, updated_at=? WHERE id=?",
|
||||
(
|
||||
"V1R1C0",
|
||||
"V1R1C0-baseline-20260706",
|
||||
"AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
||||
"git_patch_only",
|
||||
datetime.datetime.utcnow().isoformat() + "Z",
|
||||
"current",
|
||||
),
|
||||
)
|
||||
con.commit()
|
||||
con.close()
|
||||
PY
|
||||
|
||||
sudo chown -R aiman:aiman "$ROOT/runtime" "$WORKSPACE" "$ROOT/logs" "$ROOT/artifacts"
|
||||
sudo systemctl restart aios-gitops-model-api.service
|
||||
sleep 2
|
||||
curl -fsS http://127.0.0.1:18890/api/status
|
||||
echo
|
||||
python3 scripts/hermes_gitops_parallel_agents.py
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
NGINX_SITE="/etc/nginx/sites-enabled/aios-user-portal.conf"
|
||||
BACKUP_DIR="$ROOT/backups/nginx-gitops-$(date +%Y%m%d-%H%M%S)"
|
||||
LOG="$ROOT/logs/nginx-gitops-routes.log"
|
||||
mkdir -p "$BACKUP_DIR" "$ROOT/logs" "$WORKSPACE/deploy/nginx"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
cd "$WORKSPACE"
|
||||
|
||||
cat > deploy/nginx/aios-gitops-locations.conf <<'EOF'
|
||||
# BEGIN AIOS V1R1C0 GitOps short links
|
||||
location = /git { return 302 /git/; }
|
||||
location /git/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /git;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /api { return 302 /api/; }
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:18890/api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location = /base { proxy_pass http://127.0.0.1:18890/base; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /ver { proxy_pass http://127.0.0.1:18890/ver; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /diff { proxy_pass http://127.0.0.1:18890/diff; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /patch { proxy_pass http://127.0.0.1:18890/patch; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /model { proxy_pass http://127.0.0.1:18890/model; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /run { proxy_pass http://127.0.0.1:18890/run; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location = /hook { proxy_pass http://127.0.0.1:18890/hook; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
|
||||
location /hook/ {
|
||||
proxy_pass http://127.0.0.1:18890/hook/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
# END AIOS V1R1C0 GitOps short links
|
||||
EOF
|
||||
|
||||
git add deploy/nginx/aios-gitops-locations.conf active-runtime/app/aios_gitops_model_api.py
|
||||
git commit -m "feat(gitops): add nginx short links and webhook endpoint" || true
|
||||
|
||||
echo "== backup nginx site =="
|
||||
sudo cp -a "$NGINX_SITE" "$BACKUP_DIR/aios-user-portal.conf"
|
||||
|
||||
echo "== insert locations =="
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
site = Path("/etc/nginx/sites-enabled/aios-user-portal.conf")
|
||||
snippet = Path("/data/aios-gitops/workspace/deploy/nginx/aios-gitops-locations.conf").read_text()
|
||||
text = site.read_text()
|
||||
start = " # BEGIN AIOS V1R1C0 GitOps short links"
|
||||
end = " # END AIOS V1R1C0 GitOps short links"
|
||||
if start in text:
|
||||
before = text.split(start)[0]
|
||||
after = text.split(end, 1)[1]
|
||||
text = before + snippet + after
|
||||
else:
|
||||
marker = " location / {\n"
|
||||
if marker not in text:
|
||||
raise SystemExit("location marker not found")
|
||||
text = text.replace(marker, snippet + "\n" + marker, 1)
|
||||
Path("/tmp/aios-user-portal.conf.gitops").write_text(text)
|
||||
PY
|
||||
sudo cp /tmp/aios-user-portal.conf.gitops "$NGINX_SITE"
|
||||
|
||||
echo "== nginx test and reload =="
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
echo "== local route checks =="
|
||||
for path in /git/ /base /ver /model /run /hook /api/status; do
|
||||
echo "== ${path} =="
|
||||
curl -k -sS -I --resolve cnnetmkt.top:443:127.0.0.1 "https://cnnetmkt.top${path}" | sed -n '1,12p' || true
|
||||
done
|
||||
echo "NGINX_GITOPS_ROUTES_OK backup=$BACKUP_DIR"
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "== host =="
|
||||
hostname
|
||||
date -Is
|
||||
|
||||
echo "== /etc/hosts cnnetmkt =="
|
||||
grep -nE 'cnnetmkt|gitea|aios' /etc/hosts || true
|
||||
|
||||
echo "== local resolver =="
|
||||
for n in cnnetmkt.top g.cnnetmkt.top user.cnnetmkt.top portal.cnnetmkt.top; do
|
||||
echo "-- ${n}"
|
||||
getent ahosts "${n}" || true
|
||||
done
|
||||
|
||||
echo "== DoH public DNS =="
|
||||
for n in cnnetmkt.top g.cnnetmkt.top user.cnnetmkt.top portal.cnnetmkt.top; do
|
||||
echo "-- ${n}"
|
||||
curl -fsS --connect-timeout 10 --max-time 20 \
|
||||
-H 'accept: application/dns-json' \
|
||||
"https://cloudflare-dns.com/dns-query?name=${n}&type=A" || true
|
||||
echo
|
||||
done
|
||||
|
||||
echo "== local SNI HTTP/TLS =="
|
||||
for h in cnnetmkt.top g.cnnetmkt.top user.cnnetmkt.top portal.cnnetmkt.top; do
|
||||
echo "-- ${h} curl /"
|
||||
rm -f /tmp/aios-ingress-body
|
||||
curl -k -sS -D - -o /tmp/aios-ingress-body \
|
||||
--resolve "${h}:443:127.0.0.1" \
|
||||
"https://${h}/" | sed -n '1,20p' || true
|
||||
sed -n '1,16p' /tmp/aios-ingress-body 2>/dev/null || true
|
||||
echo "-- ${h} curl /git/"
|
||||
curl -k -sS -D - -o /tmp/aios-ingress-body \
|
||||
--resolve "${h}:443:127.0.0.1" \
|
||||
"https://${h}/git/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/aios-ingress-body 2>/dev/null | head -1 || true
|
||||
echo "-- ${h} cert"
|
||||
echo | openssl s_client -connect 127.0.0.1:443 -servername "${h}" 2>/dev/null \
|
||||
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2>/dev/null || true
|
||||
done
|
||||
|
||||
echo "== certbot certificates =="
|
||||
sudo certbot certificates || true
|
||||
|
||||
echo "== nginx matched config =="
|
||||
sudo nginx -T 2>/dev/null | grep -nE 'server_name|ssl_certificate|location = /git|location /git|proxy_pass http://127.0.0.1:3000' || true
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
for path in /git/ /base /ver /model /run /hook /api/status; do
|
||||
echo "== ${path} =="
|
||||
curl -k -sS --max-time 10 --resolve cnnetmkt.top:443:127.0.0.1 "https://cnnetmkt.top${path}" | head -20 || true
|
||||
done
|
||||
|
||||
echo "== direct API =="
|
||||
for path in /base /ver /model /run /hook /api/status; do
|
||||
echo "== 18890${path} =="
|
||||
curl -sS --max-time 10 "http://127.0.0.1:18890${path}" | head -10 || true
|
||||
done
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
ARTIFACT="$ROOT/artifacts/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md"
|
||||
REPORT_IN_REPO="$WORKSPACE/docs/release/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md"
|
||||
EVIDENCE="$ROOT/artifacts/AIOS_V1R1C0_FINAL_EVIDENCE.txt"
|
||||
VERSION="V1R1C0"
|
||||
TAG="V1R1C0-baseline-20260706"
|
||||
SOURCE="AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip"
|
||||
GIT_URL="https://cnnetmkt.top/git/aios/aios-v23.git"
|
||||
|
||||
mkdir -p "$ROOT/artifacts" "$ROOT/logs" "$WORKSPACE/docs/release"
|
||||
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("/data/aios-gitops")
|
||||
workspace = root / "workspace"
|
||||
db = root / "runtime" / "gitops_model.sqlite3"
|
||||
version = "V1R1C0"
|
||||
tag = "V1R1C0-baseline-20260706"
|
||||
source = "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip"
|
||||
entry = "https://cnnetmkt.top"
|
||||
git_url = "https://cnnetmkt.top/git/aios/aios-v23.git"
|
||||
short_links = ["/git", "/base", "/ver", "/diff", "/patch", "/model", "/run", "/api", "/hook"]
|
||||
|
||||
current_path = workspace / "baseline" / "current.json"
|
||||
current = {}
|
||||
if current_path.exists():
|
||||
current = json.loads(current_path.read_text(encoding="utf-8"))
|
||||
current.update({
|
||||
"version": version,
|
||||
"active_version": version,
|
||||
"baseline_tag": tag,
|
||||
"active_tag": tag,
|
||||
"source": source,
|
||||
"source_package": source,
|
||||
"mode": "gitops-online",
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_mode": "deprecated",
|
||||
"chatgpt_access": True,
|
||||
"codex_access": True,
|
||||
"git_url": git_url,
|
||||
"entry": entry,
|
||||
"short_links": short_links,
|
||||
"updated_at": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
current_path.write_text(json.dumps(current, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
version_path = workspace / "versions" / "V1R1C0.json"
|
||||
version_doc = {}
|
||||
if version_path.exists():
|
||||
version_doc = json.loads(version_path.read_text(encoding="utf-8"))
|
||||
version_doc.update({
|
||||
"version": version,
|
||||
"tag": tag,
|
||||
"baseline_tag": tag,
|
||||
"source": source,
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_mode": "deprecated",
|
||||
"git_url": git_url,
|
||||
"updated_at": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
version_path.write_text(json.dumps(version_doc, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
if db.exists():
|
||||
con = sqlite3.connect(db)
|
||||
try:
|
||||
con.execute(
|
||||
"UPDATE gitops_baseline SET active_version=?, active_tag=?, source_package=?, entry=?, update_mode=?, status=?, updated_at=? WHERE id='current'",
|
||||
(version, tag, source, entry, "git_patch_only", "active", datetime.now(timezone.utc).isoformat()),
|
||||
)
|
||||
con.execute(
|
||||
"UPDATE gitops_versions SET tag=?, status=? WHERE version=?",
|
||||
(tag, "ready", version),
|
||||
)
|
||||
con.commit()
|
||||
finally:
|
||||
con.close()
|
||||
PY
|
||||
|
||||
cd "$WORKSPACE"
|
||||
|
||||
{
|
||||
echo "== services =="
|
||||
systemctl is-active aios-gitops-model-api aios-gitea aios-hermes-router aios-hermes-executor openclaw-gateway nginx || true
|
||||
echo
|
||||
echo "== gitea =="
|
||||
curl -fsS http://127.0.0.1:3000/api/v1/version || true
|
||||
echo
|
||||
echo "== git =="
|
||||
git status --short
|
||||
git --no-pager log --oneline -8
|
||||
git tag --list 'V1R1C0*'
|
||||
git remote -v
|
||||
echo
|
||||
echo "== api health =="
|
||||
curl -fsS http://127.0.0.1:18890/health
|
||||
echo
|
||||
curl -fsS http://127.0.0.1:18890/api/status
|
||||
echo
|
||||
echo "== short links =="
|
||||
for path in /git/ /base /ver /diff /patch /model /run /api /hook /api/status; do
|
||||
tmp="$(mktemp)"
|
||||
code="$(curl -k -sS --max-time 10 --resolve cnnetmkt.top:443:127.0.0.1 -o "$tmp" -w "%{http_code}" "https://cnnetmkt.top${path}" || true)"
|
||||
title="$(grep -ao '<title>[^<]*' "$tmp" | head -1 | sed 's/<title>//' || true)"
|
||||
bytes="$(wc -c < "$tmp" | tr -d ' ')"
|
||||
printf '%s code=%s bytes=%s title=%s\n' "$path" "$code" "$bytes" "$title"
|
||||
rm -f "$tmp"
|
||||
done
|
||||
echo
|
||||
echo "== webhook smoke =="
|
||||
payload="$(mktemp)"
|
||||
cat > "$payload" <<'JSON'
|
||||
{"event":"manual-smoke","ref":"refs/heads/main","repository":{"full_name":"aios/aios-v23"}}
|
||||
JSON
|
||||
curl -fsS -H 'Content-Type: application/json' --data-binary "@${payload}" http://127.0.0.1:18890/hook/git || true
|
||||
rm -f "$payload"
|
||||
echo
|
||||
echo "== package smoke =="
|
||||
bash "$WORKSPACE/scripts/package_contract_check_v1r1c0.sh" || true
|
||||
echo
|
||||
echo "== hermes =="
|
||||
curl -fsS http://127.0.0.1:18792/internal/hermes/health || true
|
||||
echo
|
||||
echo "== openclaw =="
|
||||
openclaw --version 2>/dev/null || true
|
||||
openclaw models status --json 2>/dev/null | head -80 || true
|
||||
echo
|
||||
echo "== containers =="
|
||||
docker ps --format '{{.Names}} {{.Image}} {{.Ports}}' | grep -Ei 'webops|one-api|casdoor|nginx-proxy-manager|npm' || true
|
||||
echo
|
||||
echo "== secret scan =="
|
||||
if grep -R --binary-files=without-match --exclude-dir=.git -E 'sk-[A-Za-z0-9_-]{20,}|AIza[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{20,}' -n "$WORKSPACE" "$ROOT/logs" "$ROOT/artifacts" 2>/dev/null | head -20; then
|
||||
echo "SECRET_SCAN=FAILED"
|
||||
else
|
||||
echo "SECRET_SCAN=OK"
|
||||
fi
|
||||
} | tee "$EVIDENCE"
|
||||
|
||||
cat > "$ARTIFACT" <<EOF_REPORT
|
||||
# AIOS V1R1C0 Online Baseline Acceptance
|
||||
|
||||
AIOS_V1R1C0_ONLINE_BASELINE_STATUS=PARTIAL
|
||||
DOMAIN=https://cnnetmkt.top
|
||||
SHORT_LINKS=/git,/base,/ver,/diff,/patch,/model,/run,/api,/hook
|
||||
PROXY=NGINX_TEMP (NPM_NOT_RUNNING)
|
||||
GIT_URL=$GIT_URL
|
||||
BASELINE_VERSION=$VERSION
|
||||
BASELINE_TAG=$TAG
|
||||
WORKSPACE=$WORKSPACE
|
||||
WEBOPS_REUSED=YES
|
||||
CASDOOR_REUSED=PARTIAL
|
||||
OPENCLAW_REUSED=PARTIAL
|
||||
HERMES_REUSED=YES
|
||||
ONE_API_REUSED=YES
|
||||
GITEA_REUSED=NEW
|
||||
CUSTOM_LLM_STATUS=OK
|
||||
MODEL_PRIORITY_STATUS=OK
|
||||
RUNTIME_SELECTOR_STATUS=OK
|
||||
ACCEPTANCE_REPORT=$ARTIFACT
|
||||
KNOWN_ISSUES=NPM is not running on this host, so system nginx currently serves the short links; OpenClaw gateway is active but native openclaw agent execution still needs gateway/provider credentials; host-side DNS resolution for cnnetmkt.top is unreliable, so local validation used --resolve to 127.0.0.1; Docker Hub pull for Gitea was blocked by the existing Docker proxy path and was replaced with official Gitea binary deployment.
|
||||
NEXT_UPDATE_MODE=git_patch_only
|
||||
ZIP_UPDATE_MODE=deprecated
|
||||
|
||||
## Evidence
|
||||
|
||||
- Imported package: /data/aios-gitops/imports/$SOURCE
|
||||
- Package SHA256: dbee5d45aab02aab33e862bb9beb6115b3b43137e0fb5e00c98448ac1ecfd0f9
|
||||
- Git workspace: $WORKSPACE
|
||||
- Baseline branch: main
|
||||
- Baseline tag: $TAG
|
||||
- Git remote: $GIT_URL
|
||||
- Gitea internal version: 1.22.6
|
||||
- API service: aios-gitops-model-api.service on 127.0.0.1:18890
|
||||
- Gitea service: aios-gitea.service on 127.0.0.1:3000
|
||||
- Hermes executor health: ok
|
||||
- One API reused at: 127.0.0.1:13000
|
||||
- WebOps reused container: aios-webops-server-hw-webops-1
|
||||
- Parallel server agents: git_service_agent, entry_proxy_agent, gitops_api_uat_agent, acceptance_report_agent
|
||||
- Parallel agent report: /data/aios-gitops/artifacts/AIOS_V1R1C0_PARALLEL_AGENT_STATUS.json
|
||||
- Final evidence log: $EVIDENCE
|
||||
|
||||
## GitOps Rules
|
||||
|
||||
- ZIP update mode is deprecated after initial import.
|
||||
- Future updates must be git branch + commit + push + webhook.
|
||||
- Patches are represented as git branches and are reviewable by diff.
|
||||
- ChatGPT/Codex write path is /api/git-control-compatible endpoints exposed by the GitOps API.
|
||||
EOF_REPORT
|
||||
|
||||
cp "$ARTIFACT" "$REPORT_IN_REPO"
|
||||
|
||||
git add baseline/current.json versions/V1R1C0.json docs/release/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md
|
||||
git commit -m "docs(gitops): finalize V1R1C0 online baseline acceptance" || true
|
||||
|
||||
admin_user="aios"
|
||||
admin_pass_file="$ROOT/runtime/secrets/gitea-admin-password"
|
||||
if [ -s "$admin_pass_file" ]; then
|
||||
tmp_home="$(mktemp -d)"
|
||||
chmod 700 "$tmp_home"
|
||||
{
|
||||
echo "machine 127.0.0.1"
|
||||
echo "login $admin_user"
|
||||
echo "password $(cat "$admin_pass_file")"
|
||||
} > "$tmp_home/.netrc"
|
||||
chmod 600 "$tmp_home/.netrc"
|
||||
HOME="$tmp_home" git push http://127.0.0.1:3000/aios/aios-v23.git main
|
||||
HOME="$tmp_home" git push http://127.0.0.1:3000/aios/aios-v23.git --tags
|
||||
rm -rf "$tmp_home"
|
||||
fi
|
||||
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "$GIT_URL"
|
||||
git status --short
|
||||
echo "FINALIZE_V1R1C0_GITOPS_ACCEPTANCE_DONE"
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
LOG="$ROOT/logs/g-cnnetmkt-ingress-fix.log"
|
||||
BACKUP_DIR="$ROOT/backups/nginx-g-cnnetmkt-$(date +%Y%m%d-%H%M%S)"
|
||||
SITE="/etc/nginx/sites-available/aios-user-portal.conf"
|
||||
TRACKED="$WORKSPACE/deploy/nginx/g-cnnetmkt-gitea-server.conf"
|
||||
REPORT="$ROOT/artifacts/CNNETMKT_SSL_INGRESS_STATUS.md"
|
||||
mkdir -p "$ROOT/logs" "$BACKUP_DIR" "$WORKSPACE/deploy/nginx" "$ROOT/artifacts"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
echo "== backup =="
|
||||
sudo cp -a "$SITE" "$BACKUP_DIR/aios-user-portal.conf"
|
||||
|
||||
cat > "$TRACKED" <<'NGINX'
|
||||
# AIOS GitOps alias for https://g.cnnetmkt.top/
|
||||
# Note: this server block intentionally keeps /git/ as the Gitea app prefix,
|
||||
# matching the current Gitea ROOT_URL and avoiding a disruptive app.ini rewrite.
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name g.cnnetmkt.top;
|
||||
|
||||
access_log /var/log/nginx/aios-g-cnnetmkt.access.log;
|
||||
error_log /var/log/nginx/aios-g-cnnetmkt.error.log;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/user.cnnetmkt.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/user.cnnetmkt.top/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
location = / {
|
||||
return 302 /git/;
|
||||
}
|
||||
|
||||
location = /git {
|
||||
return 302 /git/;
|
||||
}
|
||||
|
||||
location /git/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /git;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name g.cnnetmkt.top;
|
||||
|
||||
location = / {
|
||||
return 302 https://$host/git/;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
NGINX
|
||||
|
||||
echo "== install g.cnnetmkt server block =="
|
||||
sudo cp "$TRACKED" /etc/nginx/sites-available/g-cnnetmkt-gitea.conf
|
||||
sudo ln -sfn /etc/nginx/sites-available/g-cnnetmkt-gitea.conf /etc/nginx/sites-enabled/g-cnnetmkt-gitea.conf
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
echo "== local validation before cert expand =="
|
||||
for h in g.cnnetmkt.top cnnetmkt.top user.cnnetmkt.top; do
|
||||
echo "-- $h /"
|
||||
curl -k -sS -D - -o /tmp/aios-body --resolve "$h:443:127.0.0.1" "https://$h/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/aios-body 2>/dev/null | head -1 || true
|
||||
echo "-- $h /git/"
|
||||
curl -k -sS -D - -o /tmp/aios-body --resolve "$h:443:127.0.0.1" "https://$h/git/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/aios-body 2>/dev/null | head -1 || true
|
||||
done
|
||||
|
||||
echo "== certbot dry run expand for g/user/portal =="
|
||||
CERTBOT_STATUS="not-run"
|
||||
if sudo certbot certonly --nginx --cert-name user.cnnetmkt.top \
|
||||
-d user.cnnetmkt.top -d portal.cnnetmkt.top -d g.cnnetmkt.top \
|
||||
--expand --dry-run --non-interactive --agree-tos; then
|
||||
CERTBOT_STATUS="dry-run-ok"
|
||||
else
|
||||
CERTBOT_STATUS="dry-run-failed"
|
||||
fi
|
||||
|
||||
if [ "$CERTBOT_STATUS" = "dry-run-ok" ]; then
|
||||
echo "== certbot real expand for g/user/portal =="
|
||||
if sudo certbot certonly --nginx --cert-name user.cnnetmkt.top \
|
||||
-d user.cnnetmkt.top -d portal.cnnetmkt.top -d g.cnnetmkt.top \
|
||||
--expand --non-interactive --agree-tos; then
|
||||
CERTBOT_STATUS="issued-g-user-portal"
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
else
|
||||
CERTBOT_STATUS="real-issue-failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "== final cert and route validation =="
|
||||
{
|
||||
echo "# cnnetmkt SSL ingress status"
|
||||
echo
|
||||
echo "generated_at=$(date -Is)"
|
||||
echo "g_route_status=installed"
|
||||
echo "certbot_status=$CERTBOT_STATUS"
|
||||
echo "apex_dns_status=blocked_until_cnnetmkt.top_has_A_record_to_1.95.164.183"
|
||||
echo "wildcard_status=blocked_until_dns01_provider_credentials_available"
|
||||
echo "backup_dir=$BACKUP_DIR"
|
||||
echo "tracked_nginx=$TRACKED"
|
||||
echo
|
||||
echo "## validation"
|
||||
for h in g.cnnetmkt.top cnnetmkt.top user.cnnetmkt.top; do
|
||||
echo "### $h"
|
||||
curl -k -sS -D - -o /tmp/aios-body --resolve "$h:443:127.0.0.1" "https://$h/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/aios-body 2>/dev/null | head -1 || true
|
||||
echo
|
||||
echo | openssl s_client -connect 127.0.0.1:443 -servername "$h" 2>/dev/null \
|
||||
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2>/dev/null || true
|
||||
echo
|
||||
done
|
||||
echo "## certbot"
|
||||
sudo certbot certificates || true
|
||||
} | tee "$REPORT"
|
||||
|
||||
cd "$WORKSPACE"
|
||||
git add deploy/nginx/g-cnnetmkt-gitea-server.conf
|
||||
git commit -m "fix(ingress): add g.cnnetmkt.top GitOps alias" || true
|
||||
|
||||
echo "G_CNNETMKT_INGRESS_FIX_DONE certbot_status=$CERTBOT_STATUS report=$REPORT"
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
GITEA_BIN="$ROOT/runtime/bin/gitea"
|
||||
GITEA_HOME="$ROOT/runtime/gitea-bin"
|
||||
SECRETS="$ROOT/runtime/secrets"
|
||||
LOG="$ROOT/logs/gitea-repo-push-fix.log"
|
||||
mkdir -p "$ROOT/logs"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
admin_user="aios"
|
||||
admin_pass="$(cat "$SECRETS/gitea-admin-password")"
|
||||
|
||||
echo "== endpoint probe =="
|
||||
for path in / /git/ /api/v1/version /git/api/v1/version /user/login /git/user/login; do
|
||||
echo "== ${path} =="
|
||||
curl -sS -i --max-time 8 "http://127.0.0.1:3000${path}" 2>&1 | sed -n '1,18p'
|
||||
done
|
||||
|
||||
echo "== ensure admin user =="
|
||||
"$GITEA_BIN" admin user create \
|
||||
--config "$GITEA_HOME/custom/conf/app.ini" \
|
||||
--username "$admin_user" \
|
||||
--password "$admin_pass" \
|
||||
--email "gitops-bot@cnnetmkt.top" \
|
||||
--admin \
|
||||
--must-change-password=false 2>/dev/null || true
|
||||
|
||||
echo "== create repo by internal API =="
|
||||
if ! curl -fsS -u "$admin_user:$admin_pass" "http://127.0.0.1:3000/api/v1/repos/$admin_user/aios-v23" >/dev/null 2>&1; then
|
||||
curl -fsS -u "$admin_user:$admin_pass" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"aios-v23","description":"AIOS V1R1C0 online GitOps baseline","private":false,"auto_init":false,"default_branch":"main"}' \
|
||||
"http://127.0.0.1:3000/api/v1/user/repos" >/dev/null
|
||||
fi
|
||||
curl -fsS -u "$admin_user:$admin_pass" "http://127.0.0.1:3000/api/v1/repos/$admin_user/aios-v23" | python3 -m json.tool | sed -n '1,80p'
|
||||
|
||||
echo "== push main and tags =="
|
||||
cd "$WORKSPACE"
|
||||
push_url="http://${admin_user}:${admin_pass}@127.0.0.1:3000/${admin_user}/aios-v23.git"
|
||||
git push "$push_url" main
|
||||
git push "$push_url" --tags
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "https://cnnetmkt.top/git/${admin_user}/aios-v23.git"
|
||||
|
||||
echo "== verify repo page =="
|
||||
curl -fsS "http://127.0.0.1:3000/${admin_user}/aios-v23" | grep -E "aios-v23|AIOS|V1R1C0" | head -30 || true
|
||||
git remote -v
|
||||
git log --oneline -8
|
||||
git status --short
|
||||
echo "GITEA_REPO_PUSH_OK"
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
LOG="$ROOT/logs/gitops-api-owner-fix-uat.log"
|
||||
mkdir -p "$ROOT/logs"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
echo "== fix ownership =="
|
||||
sudo chown -R aiman:aiman "$ROOT/runtime" "$WORKSPACE" "$ROOT/logs" "$ROOT/artifacts"
|
||||
|
||||
echo "== commit sanitized runner =="
|
||||
cd "$WORKSPACE"
|
||||
git add scripts/hermes_gitops_parallel_agents.py
|
||||
git commit -m "fix(gitops): sanitize parallel agent UAT secret handling" || true
|
||||
|
||||
echo "== restart API =="
|
||||
sudo systemctl restart aios-gitops-model-api.service
|
||||
sleep 3
|
||||
systemctl status aios-gitops-model-api.service --no-pager | sed -n '1,100p'
|
||||
curl -fsS http://127.0.0.1:18890/health
|
||||
echo
|
||||
curl -fsS http://127.0.0.1:18890/api/status
|
||||
echo
|
||||
|
||||
echo "== API UAT =="
|
||||
TOKEN="$(head -1 "$ROOT/runtime/secrets/gitops.tokens")"
|
||||
curl -fsS \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H 'X-AIOS-User: agent-uat' \
|
||||
-H 'X-AIOS-Role: owner' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"agent-uat","role":"owner","tier":"ultra"}' \
|
||||
http://127.0.0.1:18890/api/runtime/select
|
||||
echo
|
||||
curl -fsS \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H 'X-AIOS-User: agent-uat' \
|
||||
-H 'X-AIOS-Role: owner' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"provider_name":"agent-local-openai-compatible","base_url":"http://127.0.0.1:13000/v1","model_name":"agent-test-model","display_name":"Agent Test Model","priority":10,"api_key_secret_ref":"secret://llm/agent-uat/pre-provisioned-test"}' \
|
||||
http://127.0.0.1:18890/api/model/custom/add
|
||||
echo
|
||||
curl -fsS \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H 'X-AIOS-User: agent-uat' \
|
||||
-H 'X-AIOS-Role: owner' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"agent-uat"}' \
|
||||
http://127.0.0.1:18890/api/model/route/resolve
|
||||
echo
|
||||
|
||||
echo "== secret scan =="
|
||||
if grep -R 'redacted-test-key\|sk-[A-Za-z0-9]' -n "$WORKSPACE" "$ROOT/logs" 2>/dev/null; then
|
||||
echo "SECRET_SCAN_FAILED"
|
||||
exit 2
|
||||
fi
|
||||
echo "SECRET_SCAN_OK"
|
||||
|
||||
echo "== git status =="
|
||||
git status --short
|
||||
git log --oneline -8
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
GITEA_HOME="$ROOT/runtime/gitea-bin"
|
||||
GITEA_BIN="$ROOT/runtime/bin/gitea"
|
||||
GITEA_VERSION="1.22.6"
|
||||
GITEA_URL="https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64"
|
||||
SECRETS="$ROOT/runtime/secrets"
|
||||
LOG="$ROOT/logs/gitea-binary-install.log"
|
||||
|
||||
mkdir -p "$ROOT/logs" "$SECRETS" "$ROOT/runtime/bin" "$GITEA_HOME/custom/conf" "$GITEA_HOME/data" "$GITEA_HOME/log" "$WORKSPACE/deploy/gitea-binary"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
cd "$WORKSPACE"
|
||||
|
||||
echo "== write tracked deployment manifests =="
|
||||
cat > deploy/gitea-binary/app.ini.template <<'EOF'
|
||||
APP_NAME = AIOS GitOps
|
||||
RUN_USER = aiman
|
||||
WORK_PATH = /data/aios-gitops/runtime/gitea-bin
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
HTTP_ADDR = 127.0.0.1
|
||||
HTTP_PORT = 3000
|
||||
DOMAIN = cnnetmkt.top
|
||||
ROOT_URL = https://cnnetmkt.top/git/
|
||||
SERVE_FROM_SUB_PATH = true
|
||||
DISABLE_SSH = true
|
||||
OFFLINE_MODE = false
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = /data/aios-gitops/runtime/gitea-bin/data/gitea.db
|
||||
|
||||
[repository]
|
||||
ROOT = /data/aios-gitops/runtime/gitea-bin/data/repositories
|
||||
DEFAULT_BRANCH = main
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = __SECRET_KEY__
|
||||
INTERNAL_TOKEN = __INTERNAL_TOKEN__
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = true
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
|
||||
[webhook]
|
||||
ALLOWED_HOST_LIST = *
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = Info
|
||||
ROOT_PATH = /data/aios-gitops/runtime/gitea-bin/log
|
||||
EOF
|
||||
|
||||
cat > deploy/gitea-binary/aios-gitea.service <<EOF
|
||||
[Unit]
|
||||
Description=AIOS GitOps Gitea
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aiman
|
||||
Group=aiman
|
||||
WorkingDirectory=$GITEA_HOME
|
||||
ExecStart=$GITEA_BIN web --config $GITEA_HOME/custom/conf/app.ini
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
Environment=USER=aiman
|
||||
Environment=HOME=/home/aiman
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
git add deploy/gitea-binary/app.ini.template deploy/gitea-binary/aios-gitea.service
|
||||
git commit -m "chore(gitops): add binary Gitea deployment manifests" || true
|
||||
|
||||
echo "== download gitea binary =="
|
||||
if [ ! -x "$GITEA_BIN" ]; then
|
||||
tmp="$GITEA_BIN.tmp"
|
||||
curl -fL --retry 3 --connect-timeout 20 -o "$tmp" "$GITEA_URL"
|
||||
chmod 755 "$tmp"
|
||||
mv "$tmp" "$GITEA_BIN"
|
||||
fi
|
||||
"$GITEA_BIN" --version
|
||||
|
||||
echo "== render runtime app.ini =="
|
||||
secret_key_file="$SECRETS/gitea-secret-key"
|
||||
internal_token_file="$SECRETS/gitea-internal-token"
|
||||
admin_pass_file="$SECRETS/gitea-admin-password"
|
||||
api_token_file="$SECRETS/gitea-api-token"
|
||||
for f in "$secret_key_file" "$internal_token_file" "$admin_pass_file"; do
|
||||
if [ ! -s "$f" ]; then
|
||||
openssl rand -hex 32 > "$f"
|
||||
chmod 600 "$f"
|
||||
fi
|
||||
done
|
||||
sed \
|
||||
-e "s#__SECRET_KEY__#$(cat "$secret_key_file")#g" \
|
||||
-e "s#__INTERNAL_TOKEN__#$(cat "$internal_token_file")#g" \
|
||||
deploy/gitea-binary/app.ini.template > "$GITEA_HOME/custom/conf/app.ini"
|
||||
chmod 600 "$GITEA_HOME/custom/conf/app.ini"
|
||||
sudo chown -R aiman:aiman "$ROOT/runtime" "$WORKSPACE/deploy/gitea-binary"
|
||||
|
||||
echo "== install systemd service =="
|
||||
sudo cp deploy/gitea-binary/aios-gitea.service /etc/systemd/system/aios-gitea.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now aios-gitea.service
|
||||
|
||||
echo "== wait gitea =="
|
||||
for i in $(seq 1 90); do
|
||||
if curl -fsS http://127.0.0.1:3000/git/api/healthz >/dev/null 2>&1 || curl -fsS http://127.0.0.1:3000/git/ >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
systemctl is-active aios-gitea.service
|
||||
curl -fsS http://127.0.0.1:3000/git/api/healthz || true
|
||||
echo
|
||||
|
||||
echo "== create admin user and repo =="
|
||||
admin_user="aios"
|
||||
admin_email="gitops-bot@cnnetmkt.top"
|
||||
admin_pass="$(cat "$admin_pass_file")"
|
||||
"$GITEA_BIN" admin user create \
|
||||
--config "$GITEA_HOME/custom/conf/app.ini" \
|
||||
--username "$admin_user" \
|
||||
--password "$admin_pass" \
|
||||
--email "$admin_email" \
|
||||
--admin \
|
||||
--must-change-password=false 2>/dev/null || true
|
||||
|
||||
if [ ! -s "$api_token_file" ]; then
|
||||
"$GITEA_BIN" admin user generate-access-token \
|
||||
--config "$GITEA_HOME/custom/conf/app.ini" \
|
||||
--username "$admin_user" \
|
||||
--token-name aios-gitops-push \
|
||||
--scopes write:repository,read:repository,write:user,read:user \
|
||||
| awk '/Access token was successfully created/ {print $NF}' > "$api_token_file.tmp" || true
|
||||
if [ -s "$api_token_file.tmp" ]; then
|
||||
mv "$api_token_file.tmp" "$api_token_file"
|
||||
chmod 600 "$api_token_file"
|
||||
else
|
||||
rm -f "$api_token_file.tmp"
|
||||
fi
|
||||
fi
|
||||
|
||||
repo_api="http://127.0.0.1:3000/git/api/v1/user/repos"
|
||||
if ! curl -fsS -u "$admin_user:$admin_pass" "http://127.0.0.1:3000/git/api/v1/repos/$admin_user/aios-v23" >/dev/null 2>&1; then
|
||||
curl -fsS -u "$admin_user:$admin_pass" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"aios-v23","description":"AIOS V1R1C0 online GitOps baseline","private":false,"auto_init":false,"default_branch":"main"}' \
|
||||
"$repo_api" >/dev/null
|
||||
fi
|
||||
|
||||
echo "== push repository =="
|
||||
push_url="http://${admin_user}:${admin_pass}@127.0.0.1:3000/git/${admin_user}/aios-v23.git"
|
||||
git push "$push_url" main
|
||||
git push "$push_url" --tags
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "https://cnnetmkt.top/git/${admin_user}/aios-v23.git"
|
||||
|
||||
echo "== verify gitea repo =="
|
||||
curl -fsS "http://127.0.0.1:3000/git/${admin_user}/aios-v23" | grep -E "aios-v23|AIOS" | head -20 || true
|
||||
git status --short
|
||||
git log --oneline -8
|
||||
git remote -v
|
||||
|
||||
echo "GITEA_BINARY_INSTALL_OK version=$GITEA_VERSION url=https://cnnetmkt.top/git/${admin_user}/aios-v23"
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||
exec sudo bash "$0" "$@"
|
||||
fi
|
||||
|
||||
ROOT="/data/aios-gitops"
|
||||
WORKSPACE="$ROOT/workspace"
|
||||
LEGO_BIN="$ROOT/runtime/bin/lego"
|
||||
LEGO_PATH="/etc/letsencrypt/lego-cnnetmkt"
|
||||
LIVE_DIR="/etc/letsencrypt/live/cnnetmkt-wildcard"
|
||||
ENV_FILE="/etc/letsencrypt/huaweicloud-cnnetmkt.env"
|
||||
LOG="$ROOT/logs/cnnetmkt-wildcard-lego.log"
|
||||
REPORT="$ROOT/artifacts/CNNETMKT_WILDCARD_CERT_STATUS.md"
|
||||
TRACKED="$WORKSPACE/deploy/nginx/cnnetmkt-wildcard-certificate.md"
|
||||
BACKUP_DIR="$ROOT/backups/nginx-wildcard-$(date +%Y%m%d-%H%M%S)"
|
||||
EMAIL="admin@cnnetmkt.top"
|
||||
CERT_NAME="cnnetmkt-wildcard"
|
||||
LEGO_VERSION="v5.2.2"
|
||||
LEGO_URL="https://github.com/go-acme/lego/releases/download/${LEGO_VERSION}/lego_${LEGO_VERSION}_linux_amd64.tar.gz"
|
||||
mkdir -p "$ROOT/logs" "$ROOT/runtime/bin" "$ROOT/artifacts" "$WORKSPACE/deploy/nginx" "$BACKUP_DIR"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
redact() {
|
||||
sed -E 's/(ACCESS_KEY_ID=).+/\1[REDACTED]/; s/(SECRET_ACCESS_KEY=).+/\1[REDACTED]/'
|
||||
}
|
||||
|
||||
echo "== preflight =="
|
||||
hostname
|
||||
date -Is
|
||||
test -s "$ENV_FILE"
|
||||
sudo chmod 600 "$ENV_FILE"
|
||||
sudo chown root:root "$ENV_FILE"
|
||||
echo "env_file=$ENV_FILE"
|
||||
sudo sed -n '1,20p' "$ENV_FILE" | redact
|
||||
|
||||
echo "== install lego =="
|
||||
if [ ! -x "$LEGO_BIN" ]; then
|
||||
tmp="$(mktemp -d)"
|
||||
curl -fL --retry 3 --connect-timeout 30 -o "$tmp/lego.tar.gz" "$LEGO_URL"
|
||||
tar -xzf "$tmp/lego.tar.gz" -C "$tmp"
|
||||
install -m 0755 "$tmp/lego" "$LEGO_BIN"
|
||||
rm -rf "$tmp"
|
||||
fi
|
||||
"$LEGO_BIN" --version
|
||||
|
||||
echo "== load env =="
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$ENV_FILE"
|
||||
set +a
|
||||
: "${HUAWEICLOUD_ACCESS_KEY_ID:?missing access key id}"
|
||||
: "${HUAWEICLOUD_SECRET_ACCESS_KEY:?missing secret access key}"
|
||||
: "${HUAWEICLOUD_REGION:?missing huawei region}"
|
||||
export HUAWEICLOUD_HTTP_TIMEOUT="${HUAWEICLOUD_HTTP_TIMEOUT:-30}"
|
||||
export HUAWEICLOUD_PROPAGATION_TIMEOUT="${HUAWEICLOUD_PROPAGATION_TIMEOUT:-180}"
|
||||
export HUAWEICLOUD_POLLING_INTERVAL="${HUAWEICLOUD_POLLING_INTERVAL:-5}"
|
||||
export HUAWEICLOUD_TTL="${HUAWEICLOUD_TTL:-300}"
|
||||
|
||||
echo "== DNS state =="
|
||||
for n in cnnetmkt.top g.cnnetmkt.top user.cnnetmkt.top portal.cnnetmkt.top; do
|
||||
echo "-- $n"
|
||||
dig +time=5 +tries=1 +short "$n" A || true
|
||||
done
|
||||
|
||||
echo "== staging DNS-01 =="
|
||||
STAGING_STATUS="failed"
|
||||
if sudo --preserve-env=HUAWEICLOUD_ACCESS_KEY_ID,HUAWEICLOUD_SECRET_ACCESS_KEY,HUAWEICLOUD_REGION,HUAWEICLOUD_HTTP_TIMEOUT,HUAWEICLOUD_PROPAGATION_TIMEOUT,HUAWEICLOUD_POLLING_INTERVAL,HUAWEICLOUD_TTL \
|
||||
"$LEGO_BIN" run --path "$LEGO_PATH-staging" --email "$EMAIL" --accept-tos \
|
||||
--server "https://acme-staging-v02.api.letsencrypt.org/directory" \
|
||||
--dns huaweicloud --dns.propagation.wait 120s --cert.name "$CERT_NAME" --domains "cnnetmkt.top" --domains "*.cnnetmkt.top"; then
|
||||
STAGING_STATUS="ok"
|
||||
fi
|
||||
echo "staging_status=$STAGING_STATUS"
|
||||
if [ "$STAGING_STATUS" != "ok" ]; then
|
||||
echo "STAGING_DNS01_FAILED"
|
||||
exit 20
|
||||
fi
|
||||
|
||||
echo "== production DNS-01 =="
|
||||
PROD_STATUS="failed"
|
||||
if sudo --preserve-env=HUAWEICLOUD_ACCESS_KEY_ID,HUAWEICLOUD_SECRET_ACCESS_KEY,HUAWEICLOUD_REGION,HUAWEICLOUD_HTTP_TIMEOUT,HUAWEICLOUD_PROPAGATION_TIMEOUT,HUAWEICLOUD_POLLING_INTERVAL,HUAWEICLOUD_TTL \
|
||||
"$LEGO_BIN" run --path "$LEGO_PATH" --email "$EMAIL" --accept-tos \
|
||||
--dns huaweicloud --dns.propagation.wait 120s --cert.name "$CERT_NAME" --domains "cnnetmkt.top" --domains "*.cnnetmkt.top"; then
|
||||
PROD_STATUS="issued"
|
||||
else
|
||||
sudo --preserve-env=HUAWEICLOUD_ACCESS_KEY_ID,HUAWEICLOUD_SECRET_ACCESS_KEY,HUAWEICLOUD_REGION,HUAWEICLOUD_HTTP_TIMEOUT,HUAWEICLOUD_PROPAGATION_TIMEOUT,HUAWEICLOUD_POLLING_INTERVAL,HUAWEICLOUD_TTL \
|
||||
"$LEGO_BIN" run --path "$LEGO_PATH" --email "$EMAIL" --accept-tos \
|
||||
--dns huaweicloud --dns.propagation.wait 120s --cert.name "$CERT_NAME" --domains "cnnetmkt.top" --domains "*.cnnetmkt.top" --renew-days 60 --renew-force
|
||||
PROD_STATUS="renewed"
|
||||
fi
|
||||
|
||||
echo "== install cert material =="
|
||||
sudo mkdir -p "$LIVE_DIR"
|
||||
cert_file="$(sudo find "$LEGO_PATH/certificates" -maxdepth 1 -type f -name '*.crt' ! -name '*.issuer.crt' | head -1)"
|
||||
key_file="$(sudo find "$LEGO_PATH/certificates" -maxdepth 1 -type f -name '*.key' | head -1)"
|
||||
issuer_file="$(sudo find "$LEGO_PATH/certificates" -maxdepth 1 -type f -name '*.issuer.crt' | head -1 || true)"
|
||||
test -n "$cert_file"
|
||||
test -n "$key_file"
|
||||
sudo cp "$key_file" "$LIVE_DIR/privkey.pem"
|
||||
if [ -n "${issuer_file:-}" ] && [ -f "$issuer_file" ]; then
|
||||
sudo sh -c "cat '$cert_file' '$issuer_file' > '$LIVE_DIR/fullchain.pem'"
|
||||
sudo cp "$issuer_file" "$LIVE_DIR/chain.pem"
|
||||
else
|
||||
sudo cp "$cert_file" "$LIVE_DIR/fullchain.pem"
|
||||
fi
|
||||
sudo chmod 644 "$LIVE_DIR/fullchain.pem" "$LIVE_DIR/chain.pem" 2>/dev/null || true
|
||||
sudo chmod 600 "$LIVE_DIR/privkey.pem"
|
||||
|
||||
echo "== verify cert material =="
|
||||
sudo openssl x509 -in "$LIVE_DIR/fullchain.pem" -noout -subject -issuer -dates -ext subjectAltName
|
||||
|
||||
echo "== patch nginx cert paths =="
|
||||
sudo cp -a /etc/nginx/sites-available/aios-user-portal.conf "$BACKUP_DIR/aios-user-portal.conf"
|
||||
sudo cp -a /etc/nginx/sites-available/g-cnnetmkt-gitea.conf "$BACKUP_DIR/g-cnnetmkt-gitea.conf" 2>/dev/null || true
|
||||
sudo python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
files = [
|
||||
Path("/etc/nginx/sites-available/aios-user-portal.conf"),
|
||||
Path("/etc/nginx/sites-available/g-cnnetmkt-gitea.conf"),
|
||||
]
|
||||
for path in files:
|
||||
if not path.exists():
|
||||
continue
|
||||
text = path.read_text()
|
||||
text = text.replace(
|
||||
"/etc/letsencrypt/live/user.cnnetmkt.top/fullchain.pem",
|
||||
"/etc/letsencrypt/live/cnnetmkt-wildcard/fullchain.pem",
|
||||
)
|
||||
text = text.replace(
|
||||
"/etc/letsencrypt/live/user.cnnetmkt.top/privkey.pem",
|
||||
"/etc/letsencrypt/live/cnnetmkt-wildcard/privkey.pem",
|
||||
)
|
||||
path.write_text(text)
|
||||
PY
|
||||
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
echo "== validation =="
|
||||
for h in cnnetmkt.top g.cnnetmkt.top user.cnnetmkt.top portal.cnnetmkt.top; do
|
||||
echo "-- $h cert"
|
||||
echo | openssl s_client -connect 127.0.0.1:443 -servername "$h" 2>/dev/null \
|
||||
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2>/dev/null || true
|
||||
echo "-- $h /"
|
||||
curl -k -sS -D - -o /tmp/cnnetmkt-body --resolve "$h:443:127.0.0.1" "https://$h/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/cnnetmkt-body 2>/dev/null | head -1 || true
|
||||
echo "-- $h /git/"
|
||||
curl -k -sS -D - -o /tmp/cnnetmkt-body --resolve "$h:443:127.0.0.1" "https://$h/git/" | sed -n '1,12p' || true
|
||||
grep -ao '<title>[^<]*' /tmp/cnnetmkt-body 2>/dev/null | head -1 || true
|
||||
done
|
||||
|
||||
echo "== write renewal helper =="
|
||||
sudo tee /usr/local/sbin/renew-cnnetmkt-wildcard.sh >/dev/null <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT="/data/aios-gitops"
|
||||
LEGO_BIN="$ROOT/runtime/bin/lego"
|
||||
LEGO_PATH="/etc/letsencrypt/lego-cnnetmkt"
|
||||
LIVE_DIR="/etc/letsencrypt/live/cnnetmkt-wildcard"
|
||||
ENV_FILE="/etc/letsencrypt/huaweicloud-cnnetmkt.env"
|
||||
set -a
|
||||
. "$ENV_FILE"
|
||||
set +a
|
||||
sudo --preserve-env=HUAWEICLOUD_ACCESS_KEY_ID,HUAWEICLOUD_SECRET_ACCESS_KEY,HUAWEICLOUD_REGION,HUAWEICLOUD_HTTP_TIMEOUT,HUAWEICLOUD_PROPAGATION_TIMEOUT,HUAWEICLOUD_POLLING_INTERVAL,HUAWEICLOUD_TTL \
|
||||
"$LEGO_BIN" run --path "$LEGO_PATH" --email "admin@cnnetmkt.top" --accept-tos \
|
||||
--dns huaweicloud --dns.propagation.wait 120s --cert.name "cnnetmkt-wildcard" --domains "cnnetmkt.top" --domains "*.cnnetmkt.top" --renew-days 45
|
||||
cert_file="$(find "$LEGO_PATH/certificates" -maxdepth 1 -type f -name '*.crt' ! -name '*.issuer.crt' | head -1)"
|
||||
key_file="$(find "$LEGO_PATH/certificates" -maxdepth 1 -type f -name '*.key' | head -1)"
|
||||
cp "$cert_file" "$LIVE_DIR/fullchain.pem"
|
||||
cp "$key_file" "$LIVE_DIR/privkey.pem"
|
||||
chmod 644 "$LIVE_DIR/fullchain.pem"
|
||||
chmod 600 "$LIVE_DIR/privkey.pem"
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
SH
|
||||
sudo chmod 700 /usr/local/sbin/renew-cnnetmkt-wildcard.sh
|
||||
|
||||
sudo tee /etc/systemd/system/cnnetmkt-wildcard-renew.service >/dev/null <<'UNIT'
|
||||
[Unit]
|
||||
Description=Renew cnnetmkt wildcard certificate via Huawei Cloud DNS
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/renew-cnnetmkt-wildcard.sh
|
||||
UNIT
|
||||
|
||||
sudo tee /etc/systemd/system/cnnetmkt-wildcard-renew.timer >/dev/null <<'UNIT'
|
||||
[Unit]
|
||||
Description=Daily cnnetmkt wildcard certificate renewal check
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
RandomizedDelaySec=2h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
UNIT
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now cnnetmkt-wildcard-renew.timer
|
||||
|
||||
cat > "$TRACKED" <<EOF_TRACKED
|
||||
# cnnetmkt wildcard certificate
|
||||
|
||||
generated_at=$(date -Is)
|
||||
issuer=Let's Encrypt via lego DNS-01 Huawei Cloud
|
||||
domains=cnnetmkt.top,*.cnnetmkt.top
|
||||
live_dir=$LIVE_DIR
|
||||
nginx_sites=/etc/nginx/sites-available/aios-user-portal.conf,/etc/nginx/sites-available/g-cnnetmkt-gitea.conf
|
||||
renew_service=cnnetmkt-wildcard-renew.service
|
||||
renew_timer=cnnetmkt-wildcard-renew.timer
|
||||
secret_env=$ENV_FILE
|
||||
backup_dir=$BACKUP_DIR
|
||||
status=$PROD_STATUS
|
||||
EOF_TRACKED
|
||||
|
||||
cat > "$REPORT" <<EOF_REPORT
|
||||
# CNNETMKT wildcard certificate status
|
||||
|
||||
generated_at=$(date -Is)
|
||||
status=$PROD_STATUS
|
||||
staging_status=$STAGING_STATUS
|
||||
dns_provider=huaweicloud
|
||||
secret_storage=$ENV_FILE
|
||||
certificate_live_dir=$LIVE_DIR
|
||||
nginx_status=loaded
|
||||
backup_dir=$BACKUP_DIR
|
||||
renew_timer=cnnetmkt-wildcard-renew.timer
|
||||
|
||||
## certificate
|
||||
|
||||
$(sudo openssl x509 -in "$LIVE_DIR/fullchain.pem" -noout -subject -issuer -dates -ext subjectAltName)
|
||||
|
||||
## routes
|
||||
|
||||
- https://cnnetmkt.top/
|
||||
- https://cnnetmkt.top/git/
|
||||
- https://g.cnnetmkt.top/
|
||||
- https://g.cnnetmkt.top/git/
|
||||
- https://user.cnnetmkt.top/
|
||||
EOF_REPORT
|
||||
|
||||
cd "$WORKSPACE"
|
||||
chown -R aiman:aiman "$WORKSPACE/deploy/nginx" "$ROOT/artifacts" "$ROOT/logs"
|
||||
runuser -u aiman -- git add deploy/nginx/cnnetmkt-wildcard-certificate.md
|
||||
runuser -u aiman -- git commit -m "fix(ingress): switch cnnetmkt to wildcard certificate" || true
|
||||
|
||||
echo "CNNETMKT_WILDCARD_DONE status=$PROD_STATUS report=$REPORT"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
for v in 1.22.6 1.22.5 1.22.4 1.22.3 1.22.2 1.22.1 1.22.0; do
|
||||
url="https://dl.gitea.com/gitea/${v}/gitea-${v}-linux-amd64"
|
||||
echo "== ${v} ${url} =="
|
||||
curl -I --max-time 20 "$url" 2>&1 | sed -n '1,16p'
|
||||
done
|
||||
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import concurrent.futures
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path("/data/aios-gitops")
|
||||
WORKSPACE = ROOT / "workspace"
|
||||
AGENT_DIR = ROOT / "runtime" / "agents"
|
||||
REPORT = ROOT / "artifacts" / "AIOS_V1R1C0_PARALLEL_AGENT_STATUS.json"
|
||||
HERMES_EXECUTE = "http://127.0.0.1:18792/internal/hermes/execute"
|
||||
|
||||
|
||||
def run(cmd: str, timeout: int = 90) -> dict:
|
||||
p = subprocess.run(cmd, shell=True, text=True, capture_output=True, timeout=timeout)
|
||||
return {
|
||||
"cmd": cmd,
|
||||
"returncode": p.returncode,
|
||||
"stdout": p.stdout[-8000:],
|
||||
"stderr": p.stderr[-8000:],
|
||||
}
|
||||
|
||||
|
||||
def hermes_plan(name: str, prompt: str) -> dict:
|
||||
payload = {
|
||||
"route_id": f"wx-hermes-{time.strftime('%Y%m%d-%H%M%S')}-{name[:8].replace('_','')}",
|
||||
"route_target": "hermes",
|
||||
"route_reason": "explicit_user_prefix",
|
||||
"channel": "openclaw-weixin",
|
||||
"source": "server_parallel_agent",
|
||||
"tenant_id": "cnnetmkt",
|
||||
"user_id": f"agent:{name}",
|
||||
"user_prompt": prompt,
|
||||
}
|
||||
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
||||
req = urllib.request.Request(HERMES_EXECUTE, data=data, method="POST", headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=45) as resp:
|
||||
raw = resp.read().decode("utf-8", errors="replace")
|
||||
return {"ok": resp.status < 300, "status": resp.status, "response": json.loads(raw or "{}")}
|
||||
except Exception as exc:
|
||||
return {"ok": False, "error": f"{type(exc).__name__}: {exc}"}
|
||||
|
||||
|
||||
TASKS = {
|
||||
"git_service_agent": {
|
||||
"prompt": "AIOS V1R1C0 GitOps: 评估 Gitea 拉取失败后的 Git Web 服务补救方案,要求不直接暴露公网端口。",
|
||||
"commands": [
|
||||
"docker ps -a --format '{{.Names}} {{.Image}} {{.Status}} {{.Ports}}' | grep -E '^aios-gitea ' || true",
|
||||
"docker compose -f /data/aios-gitops/adapters/gitea/docker-compose.yml ps 2>&1 || true",
|
||||
"docker pull gitea/gitea:1.22 2>&1 | tail -80 || true",
|
||||
"apt-cache policy cgit gitweb gitea 2>/dev/null | sed -n '1,120p' || true",
|
||||
],
|
||||
},
|
||||
"entry_proxy_agent": {
|
||||
"prompt": "AIOS V1R1C0 GitOps: 探测 cnnetmkt.top 当前入口代理,区分 NPM 与裸 nginx,并给出最小安全路由方案。",
|
||||
"commands": [
|
||||
"sudo ss -lntp | grep -E ':(80|443|3000|18890|18891)' || true",
|
||||
"ps -ef | grep -Ei 'nginx|npm|proxy-manager|caddy|traefik' | grep -v grep || true",
|
||||
"sudo nginx -T 2>/tmp/aios-nginx-t.err | grep -nE 'server_name|proxy_pass|cnnetmkt|user.cnnetmkt|git|base|model|run' | head -200 || true",
|
||||
"cat /tmp/aios-nginx-t.err 2>/dev/null || true",
|
||||
],
|
||||
},
|
||||
"gitops_api_uat_agent": {
|
||||
"prompt": "AIOS V1R1C0 GitOps: 验证 GitOps API、custom LLM、priority、runtime selector 和 baseline current 状态。",
|
||||
"commands": [
|
||||
"curl -fsS http://127.0.0.1:18890/health || true",
|
||||
"curl -fsS http://127.0.0.1:18890/api/status || true",
|
||||
"TOKEN=$(head -1 /data/aios-gitops/runtime/secrets/gitops.tokens); curl -fsS -H \"Authorization: Bearer $TOKEN\" -H 'X-AIOS-User: agent-uat' -H 'X-AIOS-Role: owner' -H 'Content-Type: application/json' -d '{\"username\":\"agent-uat\",\"role\":\"owner\",\"tier\":\"ultra\"}' http://127.0.0.1:18890/api/runtime/select || true",
|
||||
"TOKEN=$(head -1 /data/aios-gitops/runtime/secrets/gitops.tokens); curl -fsS -H \"Authorization: Bearer $TOKEN\" -H 'X-AIOS-User: agent-uat' -H 'X-AIOS-Role: owner' -H 'Content-Type: application/json' -d '{\"provider_name\":\"agent-local-openai-compatible\",\"base_url\":\"http://127.0.0.1:13000/v1\",\"model_name\":\"agent-test-model\",\"display_name\":\"Agent Test Model\",\"priority\":10,\"api_key_secret_ref\":\"secret://llm/agent-uat/pre-provisioned-test\"}' http://127.0.0.1:18890/api/model/custom/add || true",
|
||||
"TOKEN=$(head -1 /data/aios-gitops/runtime/secrets/gitops.tokens); curl -fsS -H \"Authorization: Bearer $TOKEN\" -H 'X-AIOS-User: agent-uat' -H 'X-AIOS-Role: owner' -H 'Content-Type: application/json' -d '{\"username\":\"agent-uat\"}' http://127.0.0.1:18890/api/model/route/resolve || true",
|
||||
"grep -R -E 'sk-[A-Za-z0-9_-]{20,}|AIza[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{20,}' -n /data/aios-gitops/workspace /data/aios-gitops/logs 2>/dev/null || true",
|
||||
],
|
||||
},
|
||||
"acceptance_report_agent": {
|
||||
"prompt": "AIOS V1R1C0 GitOps: 汇总当前部署证据、已完成项、阻塞项,生成验收报告更新建议。",
|
||||
"commands": [
|
||||
"cd /data/aios-gitops/workspace && git status --short && git log --oneline -10 && git tag | grep V1R1C0 || true",
|
||||
"tail -40 /data/aios-gitops/logs/package_contract_check_v1r1c0.log; tail -40 /data/aios-gitops/logs/run_v1r1c0_acceptance.log; tail -40 /data/aios-gitops/logs/package_verify_full.log",
|
||||
"systemctl is-active aios-gitops-model-api.service aios-hermes-executor.service aios-hermes-router.service openclaw-gateway.service || true",
|
||||
"openclaw models status --json 2>&1 | head -120 || true",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def execute_agent(name: str, spec: dict) -> dict:
|
||||
AGENT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
log_path = AGENT_DIR / f"{name}.json"
|
||||
started = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
result = {
|
||||
"name": name,
|
||||
"started_at": started,
|
||||
"hermes_plan": hermes_plan(name, spec["prompt"]),
|
||||
"commands": [],
|
||||
}
|
||||
for cmd in spec["commands"]:
|
||||
result["commands"].append(run(cmd))
|
||||
result["finished_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
result["status"] = "completed"
|
||||
log_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> int:
|
||||
AGENT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=len(TASKS)) as pool:
|
||||
futures = {pool.submit(execute_agent, name, spec): name for name, spec in TASKS.items()}
|
||||
results = []
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
results.append(future.result())
|
||||
summary = {
|
||||
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
|
||||
"agent_count": len(results),
|
||||
"agents": [{"name": r["name"], "status": r["status"], "log": str(AGENT_DIR / f"{r['name']}.json")} for r in results],
|
||||
}
|
||||
REPORT.write_text(json.dumps({"summary": summary, "results": results}, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import concurrent.futures
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path("/data/aios-gitops")
|
||||
AGENT_DIR = ROOT / "runtime" / "agents"
|
||||
REPORT = ROOT / "artifacts" / "CNNETMKT_SSL_PARALLEL_AGENT_STATUS.json"
|
||||
HERMES_EXECUTE = "http://127.0.0.1:18792/internal/hermes/execute"
|
||||
|
||||
|
||||
def run(cmd: str, timeout: int = 90) -> dict:
|
||||
p = subprocess.run(cmd, shell=True, text=True, capture_output=True, timeout=timeout)
|
||||
return {
|
||||
"cmd": cmd,
|
||||
"returncode": p.returncode,
|
||||
"stdout": p.stdout[-12000:],
|
||||
"stderr": p.stderr[-12000:],
|
||||
}
|
||||
|
||||
|
||||
def hermes_plan(name: str, prompt: str) -> dict:
|
||||
payload = {
|
||||
"route_id": f"ssl-hermes-{time.strftime('%Y%m%d-%H%M%S')}-{name[:8]}",
|
||||
"route_target": "hermes",
|
||||
"route_reason": "server_ssl_ingress_validation",
|
||||
"channel": "openclaw-weixin",
|
||||
"source": "server_parallel_agent",
|
||||
"tenant_id": "cnnetmkt",
|
||||
"user_id": f"agent:{name}",
|
||||
"user_prompt": prompt,
|
||||
}
|
||||
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
||||
req = urllib.request.Request(HERMES_EXECUTE, data=data, method="POST", headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=45) as resp:
|
||||
raw = resp.read().decode("utf-8", errors="replace")
|
||||
return {"ok": resp.status < 300, "status": resp.status, "response": json.loads(raw or "{}")}
|
||||
except Exception as exc:
|
||||
return {"ok": False, "error": f"{type(exc).__name__}: {exc}"}
|
||||
|
||||
|
||||
TASKS = {
|
||||
"ssl_cert_agent": {
|
||||
"prompt": "Validate cnnetmkt SSL certificate coverage and identify remaining wildcard/apex blockers.",
|
||||
"commands": [
|
||||
"certbot certificates 2>&1 | sed -n '1,160p'",
|
||||
"for h in g.cnnetmkt.top cnnetmkt.top user.cnnetmkt.top; do echo --$h; echo | openssl s_client -connect 127.0.0.1:443 -servername $h 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2>/dev/null || true; done",
|
||||
],
|
||||
},
|
||||
"ssl_nginx_agent": {
|
||||
"prompt": "Validate Nginx routing for g.cnnetmkt.top and GitOps short links without changing config.",
|
||||
"commands": [
|
||||
"nginx -t 2>&1",
|
||||
"sudo nginx -T 2>/dev/null | grep -n -A70 -B5 'server_name g.cnnetmkt.top' | sed -n '1,120p'",
|
||||
"for h in g.cnnetmkt.top cnnetmkt.top user.cnnetmkt.top; do echo --$h; curl -k -sS -D - -o /tmp/ssl-agent-body --resolve $h:443:127.0.0.1 https://$h/ | sed -n '1,12p'; grep -ao '<title>[^<]*' /tmp/ssl-agent-body | head -1 || true; done",
|
||||
],
|
||||
},
|
||||
"ssl_external_agent": {
|
||||
"prompt": "Validate externally reachable HTTPS URLs for g.cnnetmkt.top and record cnnetmkt.top failure.",
|
||||
"commands": [
|
||||
"for u in https://g.cnnetmkt.top/ https://g.cnnetmkt.top/git/ https://user.cnnetmkt.top/ https://cnnetmkt.top/ https://cnnetmkt.top/git/; do echo ==$u==; curl -sS -D - -o /tmp/ssl-agent-ext --connect-timeout 20 --max-time 30 $u | sed -n '1,12p' || true; grep -ao '<title>[^<]*' /tmp/ssl-agent-ext | head -1 || true; done",
|
||||
],
|
||||
},
|
||||
"ssl_gitops_agent": {
|
||||
"prompt": "Validate GitOps repository contains the SSL ingress change and evidence report.",
|
||||
"commands": [
|
||||
"cd /data/aios-gitops/workspace && git status --short && git --no-pager log --oneline -8",
|
||||
"cd /data/aios-gitops/workspace && test -f deploy/nginx/g-cnnetmkt-gitea-server.conf && test -f docs/ops/CNNETMKT_SSL_INGRESS_STATUS.md && echo GITOPS_SSL_DOCS_OK",
|
||||
"cd /data/aios-gitops/workspace && git ls-remote --heads http://127.0.0.1:3000/aios/aios-v23.git main",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def execute_agent(name: str, spec: dict) -> dict:
|
||||
AGENT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
result = {
|
||||
"name": name,
|
||||
"started_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
|
||||
"hermes_plan": hermes_plan(name, spec["prompt"]),
|
||||
"commands": [],
|
||||
}
|
||||
for cmd in spec["commands"]:
|
||||
result["commands"].append(run(cmd))
|
||||
result["finished_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
result["status"] = "completed"
|
||||
(AGENT_DIR / f"{name}.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=len(TASKS)) as pool:
|
||||
futures = {pool.submit(execute_agent, name, spec): name for name, spec in TASKS.items()}
|
||||
results = [future.result() for future in concurrent.futures.as_completed(futures)]
|
||||
summary = {
|
||||
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
|
||||
"agent_count": len(results),
|
||||
"agents": [{"name": r["name"], "status": r["status"], "log": str(AGENT_DIR / f"{r['name']}.json")} for r in results],
|
||||
}
|
||||
REPORT.write_text(json.dumps({"summary": summary, "results": results}, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -5,5 +5,8 @@
|
||||
"name": "MultiUser GitOps Custom LLM Baseline",
|
||||
"source": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
||||
"update_mode": "git_patch_only",
|
||||
"zip_update_mode": "deprecated_after_import"
|
||||
"zip_update_mode": "deprecated",
|
||||
"baseline_tag": "V1R1C0-baseline-20260706",
|
||||
"git_url": "https://cnnetmkt.top/git/aios/aios-v23.git",
|
||||
"updated_at": "2026-07-06T07:16:01.842715+00:00"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user