fix(commander): add ClaudeQQ approval proxy draft path (#134)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
+20
-22
@@ -1,4 +1,6 @@
|
||||
import { buildCommanderApprovalNotificationDraft, commanderApprovalNotificationPathUnavailable } from "../../src/components/microservices/host-codex-commander/src/approval-notification";
|
||||
import { commanderContract as hostCommanderContract, commanderHighRiskActions as highRiskActions } from "../../src/components/microservices/host-codex-commander/src/contract";
|
||||
import { redactText } from "../../src/components/microservices/host-codex-commander/src/redaction";
|
||||
|
||||
const requiredDryRunMessage = "This host Codex commander skeleton only supports dry-run planning; live daemon/control operations are not implemented.";
|
||||
|
||||
@@ -24,24 +26,6 @@ function isHighRiskAction(value: string): value is HighRiskAction {
|
||||
return highRiskActions.some((action) => action === value);
|
||||
}
|
||||
|
||||
function redactText(value: string): { text: string; redactionsApplied: number } {
|
||||
let redactionsApplied = 0;
|
||||
const patterns = [
|
||||
/\b(?:sk|ghp|github_pat|xoxb|xoxp|AKIA)[A-Za-z0-9_=-]{8,}\b/g,
|
||||
/\b(?:token|secret|password|passwd|authorization|cookie|api[_-]?key)\s*[:=]\s*[^,\s]+/gi,
|
||||
/\bBearer\s+[A-Za-z0-9._~+/-]+=*\b/gi,
|
||||
/https?:\/\/[^/\s]+:[^@\s]+@[^/\s]+/gi,
|
||||
];
|
||||
let text = value;
|
||||
for (const pattern of patterns) {
|
||||
text = text.replace(pattern, () => {
|
||||
redactionsApplied += 1;
|
||||
return "<redacted>";
|
||||
});
|
||||
}
|
||||
return { text, redactionsApplied };
|
||||
}
|
||||
|
||||
function commanderHelp(): Record<string, unknown> {
|
||||
return {
|
||||
command: "commander",
|
||||
@@ -59,7 +43,7 @@ function commanderHelp(): Record<string, unknown> {
|
||||
}
|
||||
|
||||
export function commanderContract(): Record<string, unknown> {
|
||||
return hostCommanderContract();
|
||||
return hostCommanderContract() as unknown as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function safetyBoundary(): Record<string, unknown> {
|
||||
@@ -264,6 +248,9 @@ function commanderPlan(args: string[]): Record<string, unknown> {
|
||||
claudeqqApproval: {
|
||||
mutation: false,
|
||||
commandShape: "bun scripts/cli.ts commander approval request --action <action> --dry-run",
|
||||
dryRunDraft: "Chinese plain-text ClaudeQQ approval draft, <=200 chars, no Markdown",
|
||||
liveSendBlockedBy: "notification-path-unavailable",
|
||||
backendCoreProxyOnly: "bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw",
|
||||
highRiskActions,
|
||||
},
|
||||
safetyBoundary: safetyBoundary(),
|
||||
@@ -342,6 +329,9 @@ function approvalDraftValidation(): Record<string, unknown> {
|
||||
"requiresExplicitUserApproval=true",
|
||||
"claudeqq.mutation=false",
|
||||
"claudeqq.sendImplemented=false",
|
||||
"claudeqq.notificationDraft.message is <=200 Chinese chars and plain text",
|
||||
"claudeqq.notificationPath.error=notification-path-unavailable",
|
||||
"backend-core microservice proxy command is returned instead of a local skill/powershell command",
|
||||
"reason and messageTemplate are redacted",
|
||||
],
|
||||
noRuntimeSideEffects: [
|
||||
@@ -423,7 +413,7 @@ function commanderSmoke(args: string[]): Record<string, unknown> {
|
||||
"operator explicitly names the exact live action and target session/task/service",
|
||||
"current source-contract smoke and skeleton contract tests are green",
|
||||
"risk review confirms no token output, no direct database patch, and no backend restart bypass",
|
||||
"ClaudeQQ approval draft is reviewed, sent by an authorized future path, and matched to an explicit approval id",
|
||||
"ClaudeQQ approval draft is reviewed, any authorized send uses backend-core /api/microservices/claudeqq/proxy, and the reply is matched to an explicit approval id",
|
||||
"rollback and observation steps are written before enabling any daemon or bridge",
|
||||
],
|
||||
};
|
||||
@@ -458,6 +448,8 @@ function commanderApprovalRequest(args: string[]): Record<string, unknown> {
|
||||
const rawReason = optionValue(args, "--reason") ?? "operator-supplied reason required before live execution";
|
||||
const reason = redactText(rawReason);
|
||||
const taskId = optionValue(args, "--task-id") ?? null;
|
||||
const notificationDraft = buildCommanderApprovalNotificationDraft({ action, taskId, reason: rawReason });
|
||||
const notificationPath = commanderApprovalNotificationPathUnavailable(notificationDraft.message);
|
||||
return {
|
||||
ok: true,
|
||||
phase: "source-contract",
|
||||
@@ -468,12 +460,18 @@ function commanderApprovalRequest(args: string[]): Record<string, unknown> {
|
||||
reason: reason.text,
|
||||
redactionsApplied: reason.redactionsApplied,
|
||||
requiresExplicitUserApproval: true,
|
||||
notificationDraft,
|
||||
claudeqq: {
|
||||
mutation: false,
|
||||
endpointShape: "POST /api/microservices/claudeqq/proxy/api/push/text",
|
||||
endpointShape: `POST ${notificationPath.servicePath}`,
|
||||
fallbackEndpointShape: `POST ${notificationPath.fallbackServicePath}`,
|
||||
target: "configured primary user private chat",
|
||||
messageTemplate: `Approval required for ${action}. Reason: ${reason.text}. Reply with explicit approval id before execution.`,
|
||||
messageTemplate: notificationDraft.message,
|
||||
sendImplemented: false,
|
||||
dryRunNoClaudeQqSend: true,
|
||||
notificationDraft,
|
||||
notificationPath,
|
||||
noLocalSkillFallback: true,
|
||||
},
|
||||
approvalRecordShape: {
|
||||
id: "commander-approval-<stable-id>",
|
||||
|
||||
Reference in New Issue
Block a user