@canonmsg/codex-plugin 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-server-approval.d.ts +32 -0
- package/dist/app-server-approval.js +168 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/permission-mode.d.ts +2 -0
- package/dist/permission-mode.js +2 -2
- package/package.json +3 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk } from '@canonmsg/core';
|
|
2
|
+
/**
|
|
3
|
+
* Mapping for Codex app-server native approval requests.
|
|
4
|
+
*
|
|
5
|
+
* The current Canon Codex host still uses `codex exec --json`, which cannot
|
|
6
|
+
* block on these methods. Keep this as metadata/decision plumbing for the
|
|
7
|
+
* future app-server transport, not as an advertised `exec --json` UI mode.
|
|
8
|
+
*/
|
|
9
|
+
export type CodexAppServerApprovalMethod = 'item/commandExecution/requestApproval' | 'item/fileChange/requestApproval' | 'item/permissions/requestApproval' | 'execCommandApproval' | 'applyPatchApproval';
|
|
10
|
+
export interface CodexNativeApprovalRequest {
|
|
11
|
+
method: CodexAppServerApprovalMethod;
|
|
12
|
+
params: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface CanonCodexApprovalRequest {
|
|
15
|
+
toolName: string;
|
|
16
|
+
toolInput: Record<string, unknown>;
|
|
17
|
+
toolSummary: string;
|
|
18
|
+
category: ApprovalRequestCategory;
|
|
19
|
+
risk: ApprovalRisk;
|
|
20
|
+
riskLevel: 'normal' | 'destructive';
|
|
21
|
+
native: ApprovalNativeRequestMetadata;
|
|
22
|
+
details: ApprovalRequestDetail[];
|
|
23
|
+
}
|
|
24
|
+
export type CodexApprovalDecision = {
|
|
25
|
+
decision: 'accept';
|
|
26
|
+
} | {
|
|
27
|
+
decision: 'acceptForSession';
|
|
28
|
+
} | {
|
|
29
|
+
decision: 'decline';
|
|
30
|
+
};
|
|
31
|
+
export declare function mapCodexAppServerApprovalRequest(request: CodexNativeApprovalRequest): CanonCodexApprovalRequest | null;
|
|
32
|
+
export declare function mapCanonApprovalResultToCodexDecision(result: ApprovalResult): CodexApprovalDecision;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
3
|
+
}
|
|
4
|
+
function readString(record, keys) {
|
|
5
|
+
for (const key of keys) {
|
|
6
|
+
const value = record[key];
|
|
7
|
+
if (typeof value === 'string' && value.trim())
|
|
8
|
+
return value.trim();
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
function firstString(value) {
|
|
13
|
+
if (typeof value === 'string' && value.trim())
|
|
14
|
+
return value.trim();
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map((entry) => (typeof entry === 'string' ? entry.trim() : '')).find(Boolean);
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
function stringifyPreview(value, maxLength = 500) {
|
|
21
|
+
if (typeof value === 'string')
|
|
22
|
+
return value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
|
|
23
|
+
const json = JSON.stringify(value ?? {});
|
|
24
|
+
return json.length > maxLength ? `${json.slice(0, maxLength - 3)}...` : json;
|
|
25
|
+
}
|
|
26
|
+
function nativeHandle(method, params) {
|
|
27
|
+
const native = {
|
|
28
|
+
runtime: 'codex',
|
|
29
|
+
method,
|
|
30
|
+
};
|
|
31
|
+
const requestId = readString(params, ['requestId', 'id', 'callId']);
|
|
32
|
+
const provider = readString(params, ['provider']);
|
|
33
|
+
const origin = readString(params, ['origin']);
|
|
34
|
+
const surface = readString(params, ['surface']);
|
|
35
|
+
const threadId = readString(params, ['threadId', 'conversationId']);
|
|
36
|
+
const turnId = readString(params, ['turnId']);
|
|
37
|
+
const runId = readString(params, ['runId']);
|
|
38
|
+
const itemId = readString(params, ['itemId']);
|
|
39
|
+
const toolCallId = readString(params, ['toolCallId', 'toolUseId']);
|
|
40
|
+
const approvalId = readString(params, ['approvalId']);
|
|
41
|
+
const pluginId = readString(params, ['pluginId']);
|
|
42
|
+
const sessionKey = readString(params, ['sessionKey', 'sessionId']);
|
|
43
|
+
const cwd = readString(params, ['cwd', 'workingDirectory']);
|
|
44
|
+
const model = readString(params, ['model']);
|
|
45
|
+
if (requestId)
|
|
46
|
+
native.requestId = requestId;
|
|
47
|
+
if (provider)
|
|
48
|
+
native.provider = provider;
|
|
49
|
+
if (origin)
|
|
50
|
+
native.origin = origin;
|
|
51
|
+
if (surface)
|
|
52
|
+
native.surface = surface;
|
|
53
|
+
if (threadId)
|
|
54
|
+
native.threadId = threadId;
|
|
55
|
+
if (turnId)
|
|
56
|
+
native.turnId = turnId;
|
|
57
|
+
if (runId)
|
|
58
|
+
native.runId = runId;
|
|
59
|
+
if (itemId)
|
|
60
|
+
native.itemId = itemId;
|
|
61
|
+
if (toolCallId)
|
|
62
|
+
native.toolCallId = toolCallId;
|
|
63
|
+
if (approvalId)
|
|
64
|
+
native.approvalId = approvalId;
|
|
65
|
+
if (pluginId)
|
|
66
|
+
native.pluginId = pluginId;
|
|
67
|
+
if (sessionKey)
|
|
68
|
+
native.sessionKey = sessionKey;
|
|
69
|
+
if (cwd)
|
|
70
|
+
native.cwd = cwd;
|
|
71
|
+
if (model)
|
|
72
|
+
native.model = model;
|
|
73
|
+
return native;
|
|
74
|
+
}
|
|
75
|
+
function commandRisk(command) {
|
|
76
|
+
if (/\brm\s+(-rf?|--force)|\bgit\s+reset\s+--hard|\bgit\s+push\s+--force|\bdrop\s+(table|database)\b/i.test(command)) {
|
|
77
|
+
return 'destructive';
|
|
78
|
+
}
|
|
79
|
+
return 'high';
|
|
80
|
+
}
|
|
81
|
+
function detail(label, value, monospace = true) {
|
|
82
|
+
return value ? [{ label, value, monospace }] : [];
|
|
83
|
+
}
|
|
84
|
+
function mapCommandRequest(method, params) {
|
|
85
|
+
const command = readString(params, ['command', 'cmd'])
|
|
86
|
+
?? firstString(params.argv)
|
|
87
|
+
?? firstString(params.args)
|
|
88
|
+
?? stringifyPreview(params);
|
|
89
|
+
const cwd = readString(params, ['cwd', 'workingDirectory']);
|
|
90
|
+
const risk = commandRisk(command);
|
|
91
|
+
return {
|
|
92
|
+
toolName: 'codex.command',
|
|
93
|
+
toolInput: {
|
|
94
|
+
command,
|
|
95
|
+
...(cwd ? { cwd } : {}),
|
|
96
|
+
},
|
|
97
|
+
toolSummary: command,
|
|
98
|
+
category: 'command',
|
|
99
|
+
risk,
|
|
100
|
+
riskLevel: risk === 'destructive' ? 'destructive' : 'normal',
|
|
101
|
+
native: nativeHandle(method, params),
|
|
102
|
+
details: [
|
|
103
|
+
...detail('Command', command),
|
|
104
|
+
...detail('Working directory', cwd),
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function mapFileRequest(method, params) {
|
|
109
|
+
const path = readString(params, ['path', 'filePath', 'targetPath']);
|
|
110
|
+
const summary = path
|
|
111
|
+
?? firstString(params.paths)
|
|
112
|
+
?? stringifyPreview(params.changes ?? params.patch ?? params);
|
|
113
|
+
const patch = readString(params, ['patch', 'diff']);
|
|
114
|
+
const destructive = /\b(delete|remove|unlink)\b/i.test(stringifyPreview(params, 1000));
|
|
115
|
+
return {
|
|
116
|
+
toolName: method === 'applyPatchApproval' ? 'codex.applyPatch' : 'codex.fileChange',
|
|
117
|
+
toolInput: {
|
|
118
|
+
...(path ? { path } : {}),
|
|
119
|
+
...(patch ? { patch } : {}),
|
|
120
|
+
},
|
|
121
|
+
toolSummary: summary,
|
|
122
|
+
category: 'file',
|
|
123
|
+
risk: destructive ? 'destructive' : 'high',
|
|
124
|
+
riskLevel: destructive ? 'destructive' : 'normal',
|
|
125
|
+
native: nativeHandle(method, params),
|
|
126
|
+
details: [
|
|
127
|
+
...detail('Path', path),
|
|
128
|
+
...detail('Patch', patch),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function mapPermissionRequest(method, params) {
|
|
133
|
+
const action = readString(params, ['action', 'permission', 'toolName', 'tool'])
|
|
134
|
+
?? stringifyPreview(params);
|
|
135
|
+
return {
|
|
136
|
+
toolName: 'codex.permission',
|
|
137
|
+
toolInput: { action },
|
|
138
|
+
toolSummary: action,
|
|
139
|
+
category: 'tool',
|
|
140
|
+
risk: 'normal',
|
|
141
|
+
riskLevel: 'normal',
|
|
142
|
+
native: nativeHandle(method, params),
|
|
143
|
+
details: detail('Permission', action, false),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export function mapCodexAppServerApprovalRequest(request) {
|
|
147
|
+
if (!isRecord(request.params))
|
|
148
|
+
return null;
|
|
149
|
+
if (request.method === 'item/commandExecution/requestApproval'
|
|
150
|
+
|| request.method === 'execCommandApproval') {
|
|
151
|
+
return mapCommandRequest(request.method, request.params);
|
|
152
|
+
}
|
|
153
|
+
if (request.method === 'item/fileChange/requestApproval'
|
|
154
|
+
|| request.method === 'applyPatchApproval') {
|
|
155
|
+
return mapFileRequest(request.method, request.params);
|
|
156
|
+
}
|
|
157
|
+
if (request.method === 'item/permissions/requestApproval') {
|
|
158
|
+
return mapPermissionRequest(request.method, request.params);
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
export function mapCanonApprovalResultToCodexDecision(result) {
|
|
163
|
+
if (result.decision === 'deny')
|
|
164
|
+
return { decision: 'decline' };
|
|
165
|
+
if (result.sessionRule)
|
|
166
|
+
return { decision: 'acceptForSession' };
|
|
167
|
+
return { decision: 'accept' };
|
|
168
|
+
}
|
package/dist/host.js
CHANGED
|
@@ -413,10 +413,12 @@ export async function main() {
|
|
|
413
413
|
]);
|
|
414
414
|
return buildHydratedInboundContext({
|
|
415
415
|
agentId,
|
|
416
|
+
conversationId: input.conversationId,
|
|
416
417
|
conversation,
|
|
417
418
|
page,
|
|
418
419
|
activeSelfContextId: input.activeSelfContextId,
|
|
419
420
|
selfContexts: input.selfContexts,
|
|
421
|
+
provenance: input.provenance,
|
|
420
422
|
message: input.message,
|
|
421
423
|
senderName: input.senderName,
|
|
422
424
|
isOwner: input.isOwner,
|
|
@@ -687,6 +689,7 @@ export async function main() {
|
|
|
687
689
|
isOwner: input.isOwner,
|
|
688
690
|
activeSelfContextId: input.activeSelfContextId,
|
|
689
691
|
selfContexts: input.selfContexts,
|
|
692
|
+
provenance: input.provenance,
|
|
690
693
|
hydratedPage: input.hydratedPage,
|
|
691
694
|
});
|
|
692
695
|
const behavior = input.behavior ?? hydrated.behavior;
|
|
@@ -1096,6 +1099,7 @@ export async function main() {
|
|
|
1096
1099
|
behavior: payload.behavior,
|
|
1097
1100
|
activeSelfContextId: payload.activeSelfContextId,
|
|
1098
1101
|
selfContexts: payload.selfContexts,
|
|
1102
|
+
provenance: payload.provenance,
|
|
1099
1103
|
});
|
|
1100
1104
|
if (message.id) {
|
|
1101
1105
|
saveRuntimeSessionState(runtimeId, {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { main as hostMain } from './host.js';
|
|
2
2
|
export { main as registerMain } from './register.js';
|
|
3
3
|
export { main as setupMain } from './setup.js';
|
|
4
|
+
export { mapCanonApprovalResultToCodexDecision, mapCodexAppServerApprovalRequest, } from './app-server-approval.js';
|
|
5
|
+
export type { CanonCodexApprovalRequest, CodexAppServerApprovalMethod, CodexApprovalDecision, CodexNativeApprovalRequest, } from './app-server-approval.js';
|
package/dist/index.js
CHANGED
|
@@ -11,10 +11,12 @@ export declare const CODEX_PERMISSION_OPTIONS: readonly [{
|
|
|
11
11
|
readonly value: "full-auto";
|
|
12
12
|
readonly label: "Full auto";
|
|
13
13
|
readonly description: "Automatically run workspace-write actions with lower-friction approvals.";
|
|
14
|
+
readonly ownerOnly: true;
|
|
14
15
|
}, {
|
|
15
16
|
readonly value: "bypass";
|
|
16
17
|
readonly label: "Bypass (dangerous)";
|
|
17
18
|
readonly description: "Skip Codex approval and sandbox protection. Use only in an externally sandboxed environment.";
|
|
19
|
+
readonly ownerOnly: true;
|
|
18
20
|
}];
|
|
19
21
|
export type CodexPermissionMode = (typeof CODEX_PERMISSION_OPTIONS)[number]['value'];
|
|
20
22
|
export type CodexApprovalShape = {
|
package/dist/permission-mode.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const CODEX_PERMISSION_OPTIONS = [
|
|
2
2
|
{ value: 'readonly', label: 'Read-only', description: 'Inspect files and project state without making changes.' },
|
|
3
3
|
{ value: 'workspace', label: 'Workspace-write', description: 'Edit inside the selected workspace using Codex CLI sandboxing.' },
|
|
4
|
-
{ value: 'full-auto', label: 'Full auto', description: 'Automatically run workspace-write actions with lower-friction approvals.' },
|
|
5
|
-
{ value: 'bypass', label: 'Bypass (dangerous)', description: 'Skip Codex approval and sandbox protection. Use only in an externally sandboxed environment.' },
|
|
4
|
+
{ value: 'full-auto', label: 'Full auto', description: 'Automatically run workspace-write actions with lower-friction approvals.', ownerOnly: true },
|
|
5
|
+
{ value: 'bypass', label: 'Bypass (dangerous)', description: 'Skip Codex approval and sandbox protection. Use only in an externally sandboxed environment.', ownerOnly: true },
|
|
6
6
|
];
|
|
7
7
|
export function mapCanonPermissionToCodex(mode) {
|
|
8
8
|
switch (mode) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.5",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"prepack": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@canonmsg/agent-sdk": "^1.
|
|
33
|
-
"@canonmsg/core": "^0.
|
|
32
|
+
"@canonmsg/agent-sdk": "^1.5.0",
|
|
33
|
+
"@canonmsg/core": "^0.19.1"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=18.0.0"
|