Compare commits
2
Commits
b1fd9db1dd
...
33935b5f67
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33935b5f67 | ||
|
|
569d258b47 |
@@ -160,10 +160,23 @@ def patches():
|
||||
|
||||
class H(BaseHTTPRequestHandler):
|
||||
server_version='AIOSChatGPTGitOps/V1R1C0'
|
||||
def sendj(self, data, code=200):
|
||||
b=json.dumps(data, ensure_ascii=False, indent=2).encode(); self.send_response(code); self.send_header('Content-Type','application/json; charset=utf-8'); self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b)
|
||||
def sendt(self, text, ctype='text/plain; charset=utf-8', code=200):
|
||||
b=text.encode(); self.send_response(code); self.send_header('Content-Type',ctype); self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b)
|
||||
def compat_headers(self):
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, HEAD, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Authorization, Content-Type, X-AIOS-Token')
|
||||
self.send_header('Cache-Control', 'no-store')
|
||||
def send_empty(self, code=204, ctype='application/json; charset=utf-8'):
|
||||
self.send_response(code)
|
||||
self.send_header('Content-Type', ctype)
|
||||
self.compat_headers()
|
||||
self.send_header('Content-Length', '0')
|
||||
self.end_headers()
|
||||
def sendj(self, data, code=200, head=False):
|
||||
b=json.dumps(data, ensure_ascii=False, indent=2).encode(); self.send_response(code); self.send_header('Content-Type','application/json; charset=utf-8'); self.compat_headers(); self.send_header('Content-Length',str(len(b))); self.end_headers()
|
||||
if not head: self.wfile.write(b)
|
||||
def sendt(self, text, ctype='text/plain; charset=utf-8', code=200, head=False):
|
||||
b=text.encode(); self.send_response(code); self.send_header('Content-Type',ctype); self.compat_headers(); self.send_header('Content-Length',str(len(b))); self.end_headers()
|
||||
if not head: self.wfile.write(b)
|
||||
def body(self):
|
||||
n=int(self.headers.get('Content-Length','0') or '0'); raw=self.rfile.read(n).decode() if n else '{}'
|
||||
try: return json.loads(raw or '{}')
|
||||
@@ -178,6 +191,18 @@ class H(BaseHTTPRequestHandler):
|
||||
if not a['ok']: self.sendj({'error':'unauthorized','hint':'Bearer token required'},401); return None
|
||||
if '*' not in a['scopes'] and scope not in a['scopes']: self.sendj({'error':'forbidden','required_scope':scope,'account':a['account']},403); return None
|
||||
return a
|
||||
def do_OPTIONS(self):
|
||||
return self.send_empty(204)
|
||||
def do_HEAD(self):
|
||||
raw_path = urllib.parse.urlparse(self.path).path
|
||||
path = raw_path.rstrip('/') if raw_path != '/' else '/'
|
||||
if path in ('/api/openapi.yaml','/api/openapi-chatgpt-actions.yaml','/api/schema'):
|
||||
if OPENAPI.exists():
|
||||
return self.sendt(OPENAPI.read_text(encoding='utf-8'), 'application/yaml; charset=utf-8', 200, head=True)
|
||||
return self.send_empty(404)
|
||||
known={'/health','/api/health','/api','/api/status','/api/base','/api/baseline','/base','/api/ver','/api/versions','/ver','/api/diff','/diff','/api/patch','/api/patches','/api/branches','/patch','/api/model','/model','/api/run','/api/runtime-status','/run','/api/action-monitor','/action-monitor','/api/tag','/api/create-tag','/api/tag-request','/api/tag-approval-request','/api/stage-tag-request'}
|
||||
if path in known: return self.send_empty(200)
|
||||
return self.send_empty(404)
|
||||
def do_GET(self):
|
||||
u=urllib.parse.urlparse(self.path); path=u.path.rstrip('/') if u.path != '/' else u.path; q=urllib.parse.parse_qs(u.query)
|
||||
try:
|
||||
@@ -304,7 +329,7 @@ class H(BaseHTTPRequestHandler):
|
||||
b=checkout(data.get('branch') or f'patch/nl-{uid("nl")}', 'main', True); doc={'id':uid('nl'),'text':text,'intent':intent,'action_plan':plan,'execute':False,'owner_approval_required':True,'created_by':a['account'],'created_at':now()}; r=write_json(f'patches/nl/{safe(doc["id"])}.json', doc); c=commit([r], f'chore(chatgpt): record nl plan {intent}'); self.sendj({'status':'planned','branch':b,'request':doc,'commit':c})
|
||||
|
||||
def api_spec():
|
||||
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run'],'write':['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True}}
|
||||
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run'],'write':['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag-request','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True}}
|
||||
|
||||
def main():
|
||||
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
|
||||
|
||||
@@ -13,6 +13,32 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: opaque-token
|
||||
schemas:
|
||||
BaselineResponse:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
active_version: {type: string}
|
||||
active_tag: {type: string}
|
||||
mode: {type: string}
|
||||
git: {type: string}
|
||||
VersionsResponse:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
tags:
|
||||
type: array
|
||||
items: {type: string}
|
||||
versions_dir:
|
||||
type: array
|
||||
items: {type: string}
|
||||
head: {type: string}
|
||||
RuntimeStatusResponse:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
service: {type: string}
|
||||
status: {type: string}
|
||||
workspace_branch: {type: string}
|
||||
BranchRequest:
|
||||
type: object
|
||||
required: [branch]
|
||||
@@ -117,12 +143,22 @@ paths:
|
||||
get:
|
||||
operationId: getBaseline
|
||||
summary: Read current AIOS baseline
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/BaselineResponse'}
|
||||
/api/ver:
|
||||
get:
|
||||
operationId: listVersions
|
||||
summary: List GitOps versions
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/VersionsResponse'}
|
||||
/api/diff:
|
||||
get:
|
||||
operationId: getDiff
|
||||
@@ -169,7 +205,12 @@ paths:
|
||||
get:
|
||||
operationId: getRuntimeStatus
|
||||
summary: Read runtime status
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/RuntimeStatusResponse'}
|
||||
/api/branch:
|
||||
post:
|
||||
operationId: createBranch
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "nl-1783443572161-fcbaed22",
|
||||
"text": "Owner authorized granting gitops-write-bot the tag:create scope required for V1R2C3 release promotion. Create an auditable GitOps permission change plan only. Do not write or expose tokens, secrets, credentials, or private keys. After scope is actually applied by the permission controller, retry V1R2C3 tag creation and preserve V1R2C2 rollback path.",
|
||||
"intent": "rollback_request",
|
||||
"action_plan": [
|
||||
"commit rollback request",
|
||||
"wait owner/admin approval"
|
||||
],
|
||||
"execute": false,
|
||||
"owner_approval_required": true,
|
||||
"created_by": "gitops-write-bot",
|
||||
"created_at": "2026-07-07T16:59:32Z"
|
||||
}
|
||||
@@ -35,6 +35,7 @@ READY_FOR_OWNER_REIMPORT
|
||||
- Schema flag check:
|
||||
- `/api/tag-request` -> `createTagApprovalRequest` -> `x-openai-isConsequential: false`
|
||||
- `/api/tag` -> `requestOrCreateTag` -> `x-openai-isConsequential: true`
|
||||
- Package contract check includes tag approval route assertions.
|
||||
|
||||
## Owner Retest
|
||||
|
||||
|
||||
@@ -23,4 +23,9 @@ python3 -m py_compile \
|
||||
"$ROOT/adapters/openclaw/openclaw_gitops_commands.py" \
|
||||
"$ROOT/adapters/hermes/hermes_gitops_nl_router.py"
|
||||
grep -q "/api/branch" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
grep -q "/api/tag-request" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
grep -q "operationId: createTagApprovalRequest" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
grep -q "x-openai-isConsequential: false" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
grep -q "operationId: requestOrCreateTag" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
grep -q "x-openai-isConsequential: true" "$ROOT/docs/gitops/openapi-chatgpt-actions.yaml"
|
||||
echo "PACKAGE_CONTRACT_OK $(cat "$ROOT/VERSION")"
|
||||
Reference in New Issue
Block a user