@canonmsg/codex-plugin 0.6.0 → 0.6.2
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/cli-entry.d.ts +2 -0
- package/dist/cli-entry.js +16 -0
- package/dist/host-runtime.d.ts +0 -3
- package/dist/host-runtime.js +4 -15
- package/dist/host.d.ts +1 -1
- package/dist/host.js +38 -12
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/permission-mode.d.ts +31 -0
- package/dist/permission-mode.js +59 -0
- package/dist/register.d.ts +1 -1
- package/dist/register.js +78 -71
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +36 -29
- package/package.json +11 -5
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
export function isDirectExecution(moduleUrl) {
|
|
4
|
+
const entry = process.argv[1];
|
|
5
|
+
if (!entry)
|
|
6
|
+
return false;
|
|
7
|
+
return pathToFileURL(resolve(entry)).href === moduleUrl;
|
|
8
|
+
}
|
|
9
|
+
export function runCli(moduleUrl, main, onError) {
|
|
10
|
+
if (!isDirectExecution(moduleUrl)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
Promise.resolve()
|
|
14
|
+
.then(() => main())
|
|
15
|
+
.catch(onError);
|
|
16
|
+
}
|
package/dist/host-runtime.d.ts
CHANGED
|
@@ -28,9 +28,6 @@ export interface HostInboundParticipantContext {
|
|
|
28
28
|
type HostInboundMessage = {
|
|
29
29
|
text?: string | null;
|
|
30
30
|
contentType?: CanonMessage['contentType'] | null;
|
|
31
|
-
audioUrl?: string | null;
|
|
32
|
-
audioDurationMs?: number | null;
|
|
33
|
-
imageUrl?: string | null;
|
|
34
31
|
attachments?: CanonMessage['attachments'];
|
|
35
32
|
senderType?: CanonMessage['senderType'];
|
|
36
33
|
mentions?: string[] | null;
|
package/dist/host-runtime.js
CHANGED
|
@@ -49,21 +49,10 @@ export function renderCanonHostInboundContent(message, materialized) {
|
|
|
49
49
|
const body = message.text || '';
|
|
50
50
|
const placeholders = [];
|
|
51
51
|
const attachments = message.attachments ?? [];
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
placeholders.push(describeAttachment(att, mat));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else if (message.contentType === 'audio' && message.audioUrl) {
|
|
60
|
-
const duration = message.audioDurationMs
|
|
61
|
-
? ` (${Math.round(message.audioDurationMs / 1000)}s)`
|
|
62
|
-
: '';
|
|
63
|
-
placeholders.push(`[Voice message${duration}]`);
|
|
64
|
-
}
|
|
65
|
-
else if (message.contentType === 'image' && message.imageUrl) {
|
|
66
|
-
placeholders.push('[Image attached]');
|
|
52
|
+
for (let i = 0; i < attachments.length; i += 1) {
|
|
53
|
+
const att = attachments[i];
|
|
54
|
+
const mat = materialized?.find((m) => m.index === i) ?? null;
|
|
55
|
+
placeholders.push(describeAttachment(att, mat));
|
|
67
56
|
}
|
|
68
57
|
const rendered = [...placeholders, body].filter(Boolean).join('\n');
|
|
69
58
|
return rendered || '[Empty message]';
|
package/dist/host.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export declare function main(): Promise<void>;
|
package/dist/host.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setDefaultResultOrder } from 'node:dns';
|
|
3
|
-
setDefaultResultOrder('ipv4first');
|
|
4
3
|
import { randomUUID } from 'node:crypto';
|
|
5
4
|
import { parseArgs } from 'node:util';
|
|
6
5
|
import { getCodexImagePath, materializeMessageMedia, } from '@canonmsg/agent-sdk';
|
|
@@ -9,6 +8,8 @@ import { buildCanonHostPrompt, buildHydratedInboundContext, createConversationMe
|
|
|
9
8
|
import { buildInboundContextLines, decideAutoReply, } from './inbound-policy.js';
|
|
10
9
|
import { CodexConversationAdapter, } from './adapter.js';
|
|
11
10
|
import { clearStoredThreadId, loadStoredThreadId, saveStoredThreadId, } from './session-store.js';
|
|
11
|
+
import { deriveCodexPermissionEnvelope, mapCanonPermissionToCodex, } from './permission-mode.js';
|
|
12
|
+
import { runCli } from './cli-entry.js';
|
|
12
13
|
const MAX_SESSIONS = 12;
|
|
13
14
|
const IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
14
15
|
const HEARTBEAT_MS = 30_000;
|
|
@@ -42,7 +43,11 @@ async function publishAgentRuntime(agentId, runtime) {
|
|
|
42
43
|
await publishHostAgentRuntime(agentId, 'codex', runtime);
|
|
43
44
|
}
|
|
44
45
|
async function loadSessionConfig(conversationId, agentId) {
|
|
45
|
-
return loadHostSessionConfig({
|
|
46
|
+
return loadHostSessionConfig({
|
|
47
|
+
conversationId,
|
|
48
|
+
agentId,
|
|
49
|
+
extraStringFields: ['permissionMode'],
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
const SESSION_EXECUTION_MODE_REQUIRED = 'Session execution mode required; please select a mode before starting the session.';
|
|
48
53
|
function requireSessionExecutionMode(config) {
|
|
@@ -87,7 +92,8 @@ function formatTurnFailure(errorText) {
|
|
|
87
92
|
function sleep(ms) {
|
|
88
93
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
89
94
|
}
|
|
90
|
-
async function main() {
|
|
95
|
+
export async function main() {
|
|
96
|
+
setDefaultResultOrder('ipv4first');
|
|
91
97
|
const { values: args } = parseArgs({
|
|
92
98
|
options: {
|
|
93
99
|
cwd: { type: 'string' },
|
|
@@ -265,6 +271,14 @@ async function main() {
|
|
|
265
271
|
const sessionCwd = environment.cwd;
|
|
266
272
|
const sessionModel = config?.model ?? (typeof args.model === 'string' ? args.model : undefined);
|
|
267
273
|
const storedThreadId = loadStoredThreadId(agentId, conversationId, sessionCwd);
|
|
274
|
+
if (config?.permissionMode
|
|
275
|
+
&& !codexPermissionEnvelope.availablePermissionModes.some((option) => option.value === config.permissionMode)) {
|
|
276
|
+
throw new ExecutionEnvironmentError(`Permission mode "${config.permissionMode}" is not supported by this Codex host.`, 'This Canon host was started with stricter approval settings. Choose one of the advertised permission modes or restart the host with more permissive flags.');
|
|
277
|
+
}
|
|
278
|
+
const approvalOverride = mapCanonPermissionToCodex(config?.permissionMode);
|
|
279
|
+
const defaultSandbox = (typeof args.sandbox === 'string' ? args.sandbox : null);
|
|
280
|
+
const defaultFullAuto = Boolean(args['full-auto']);
|
|
281
|
+
const defaultBypass = Boolean(args['dangerously-bypass-approvals-and-sandbox']);
|
|
268
282
|
const session = {
|
|
269
283
|
conversationId,
|
|
270
284
|
cwd: sessionCwd,
|
|
@@ -274,15 +288,17 @@ async function main() {
|
|
|
274
288
|
threadId: storedThreadId,
|
|
275
289
|
codexBin: typeof args['codex-bin'] === 'string' ? args['codex-bin'] : 'codex',
|
|
276
290
|
model: sessionModel ?? null,
|
|
277
|
-
sandbox:
|
|
291
|
+
sandbox: approvalOverride ? approvalOverride.sandbox : defaultSandbox,
|
|
278
292
|
approvalPolicy: (typeof args['ask-for-approval'] === 'string'
|
|
279
293
|
? args['ask-for-approval']
|
|
280
294
|
: null),
|
|
281
295
|
codexProfile: typeof args['codex-profile'] === 'string' ? args['codex-profile'] : null,
|
|
282
296
|
addDirs: args['add-dir'] ?? [],
|
|
283
297
|
configOverrides: args.config ?? [],
|
|
284
|
-
fullAuto:
|
|
285
|
-
bypassApprovalsAndSandbox:
|
|
298
|
+
fullAuto: approvalOverride ? approvalOverride.fullAuto : defaultFullAuto,
|
|
299
|
+
bypassApprovalsAndSandbox: approvalOverride
|
|
300
|
+
? approvalOverride.bypassApprovalsAndSandbox
|
|
301
|
+
: defaultBypass,
|
|
286
302
|
}),
|
|
287
303
|
queue: [],
|
|
288
304
|
running: false,
|
|
@@ -334,10 +350,7 @@ async function main() {
|
|
|
334
350
|
try {
|
|
335
351
|
materialized = await materializeMessageMedia({
|
|
336
352
|
id: input.message.id,
|
|
337
|
-
attachments: input.message.attachments,
|
|
338
|
-
imageUrl: input.message.imageUrl ?? null,
|
|
339
|
-
audioUrl: input.message.audioUrl ?? null,
|
|
340
|
-
audioDurationMs: input.message.audioDurationMs ?? null,
|
|
353
|
+
attachments: input.message.attachments ?? [],
|
|
341
354
|
}, { agentId, conversationId: input.conversationId });
|
|
342
355
|
}
|
|
343
356
|
catch (error) {
|
|
@@ -538,11 +551,16 @@ async function main() {
|
|
|
538
551
|
const hostAvailableExecutionModes = allowWorktrees
|
|
539
552
|
? [...EXECUTION_ENVIRONMENT_MODES]
|
|
540
553
|
: ['locked'];
|
|
554
|
+
const codexPermissionEnvelope = deriveCodexPermissionEnvelope(args);
|
|
541
555
|
let runtimeDescriptor = {
|
|
542
556
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
543
557
|
...(typeof args.model === 'string' ? { defaultModel: args.model } : {}),
|
|
544
558
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
545
559
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
560
|
+
availablePermissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
561
|
+
...(codexPermissionEnvelope.defaultPermissionMode
|
|
562
|
+
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
563
|
+
: {}),
|
|
546
564
|
};
|
|
547
565
|
const publishRuntimeHeartbeat = async () => {
|
|
548
566
|
if (!streamConnected)
|
|
@@ -587,6 +605,10 @@ async function main() {
|
|
|
587
605
|
...(typeof args.model === 'string' ? { defaultModel: args.model } : {}),
|
|
588
606
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
589
607
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
608
|
+
availablePermissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
609
|
+
...(codexPermissionEnvelope.defaultPermissionMode
|
|
610
|
+
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
611
|
+
: {}),
|
|
590
612
|
};
|
|
591
613
|
}
|
|
592
614
|
catch {
|
|
@@ -594,6 +616,10 @@ async function main() {
|
|
|
594
616
|
defaultWorkspaceId: workspaceOptions[0]?.id,
|
|
595
617
|
availableWorkspaces: buildPublicWorkspaceOptions(workspaceOptions),
|
|
596
618
|
availableExecutionModes: hostAvailableExecutionModes,
|
|
619
|
+
availablePermissionModes: [...codexPermissionEnvelope.availablePermissionModes],
|
|
620
|
+
...(codexPermissionEnvelope.defaultPermissionMode
|
|
621
|
+
? { defaultPermissionMode: codexPermissionEnvelope.defaultPermissionMode }
|
|
622
|
+
: {}),
|
|
597
623
|
};
|
|
598
624
|
}
|
|
599
625
|
try {
|
|
@@ -661,7 +687,7 @@ async function main() {
|
|
|
661
687
|
writeState(session);
|
|
662
688
|
}
|
|
663
689
|
if (control.permissionMode) {
|
|
664
|
-
console.error(`[canon-codex] [${conversationId.slice(0, 8)}]
|
|
690
|
+
console.error(`[canon-codex] [${conversationId.slice(0, 8)}] approval mode is session-creation-only; ignoring mid-session change request (${control.permissionMode})`);
|
|
665
691
|
}
|
|
666
692
|
if (control.effort) {
|
|
667
693
|
console.error(`[canon-codex] [${conversationId.slice(0, 8)}] effort control is not mapped yet (${control.effort})`);
|
|
@@ -740,7 +766,7 @@ async function main() {
|
|
|
740
766
|
console.error('[canon-codex] Ready — sessions created on demand');
|
|
741
767
|
await new Promise(() => { });
|
|
742
768
|
}
|
|
743
|
-
|
|
769
|
+
runCli(import.meta.url, main, (error) => {
|
|
744
770
|
console.error('[canon-codex] Fatal error:', error);
|
|
745
771
|
const activeProfile = getActiveProfile();
|
|
746
772
|
if (activeProfile)
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CodexSandboxMode } from './adapter.js';
|
|
2
|
+
export declare const CODEX_PERMISSION_OPTIONS: readonly [{
|
|
3
|
+
readonly value: "readonly";
|
|
4
|
+
readonly label: "Read-only";
|
|
5
|
+
}, {
|
|
6
|
+
readonly value: "workspace";
|
|
7
|
+
readonly label: "Workspace-write";
|
|
8
|
+
}, {
|
|
9
|
+
readonly value: "full-auto";
|
|
10
|
+
readonly label: "Full auto";
|
|
11
|
+
}, {
|
|
12
|
+
readonly value: "bypass";
|
|
13
|
+
readonly label: "Bypass (dangerous)";
|
|
14
|
+
}];
|
|
15
|
+
export type CodexPermissionMode = (typeof CODEX_PERMISSION_OPTIONS)[number]['value'];
|
|
16
|
+
export type CodexApprovalShape = {
|
|
17
|
+
sandbox: CodexSandboxMode | null;
|
|
18
|
+
fullAuto: boolean;
|
|
19
|
+
bypassApprovalsAndSandbox: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type CodexPermissionEnvelope = {
|
|
22
|
+
defaultPermissionMode?: CodexPermissionMode;
|
|
23
|
+
availablePermissionModes: ReadonlyArray<(typeof CODEX_PERMISSION_OPTIONS)[number]>;
|
|
24
|
+
};
|
|
25
|
+
export declare function mapCanonPermissionToCodex(mode: string | null | undefined): CodexApprovalShape | null;
|
|
26
|
+
export declare function deriveCodexPermissionEnvelope(args: {
|
|
27
|
+
'full-auto'?: unknown;
|
|
28
|
+
'dangerously-bypass-approvals-and-sandbox'?: unknown;
|
|
29
|
+
'ask-for-approval'?: unknown;
|
|
30
|
+
sandbox?: unknown;
|
|
31
|
+
}): CodexPermissionEnvelope;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const CODEX_PERMISSION_OPTIONS = [
|
|
2
|
+
{ value: 'readonly', label: 'Read-only' },
|
|
3
|
+
{ value: 'workspace', label: 'Workspace-write' },
|
|
4
|
+
{ value: 'full-auto', label: 'Full auto' },
|
|
5
|
+
{ value: 'bypass', label: 'Bypass (dangerous)' },
|
|
6
|
+
];
|
|
7
|
+
export function mapCanonPermissionToCodex(mode) {
|
|
8
|
+
switch (mode) {
|
|
9
|
+
case 'readonly':
|
|
10
|
+
return { sandbox: 'read-only', fullAuto: false, bypassApprovalsAndSandbox: false };
|
|
11
|
+
case 'workspace':
|
|
12
|
+
return { sandbox: 'workspace-write', fullAuto: false, bypassApprovalsAndSandbox: false };
|
|
13
|
+
case 'full-auto':
|
|
14
|
+
return { sandbox: 'workspace-write', fullAuto: true, bypassApprovalsAndSandbox: false };
|
|
15
|
+
case 'bypass':
|
|
16
|
+
return { sandbox: null, fullAuto: false, bypassApprovalsAndSandbox: true };
|
|
17
|
+
default:
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function codexOptionsThrough(mode) {
|
|
22
|
+
const index = CODEX_PERMISSION_OPTIONS.findIndex((option) => option.value === mode);
|
|
23
|
+
return index >= 0 ? CODEX_PERMISSION_OPTIONS.slice(0, index + 1) : [];
|
|
24
|
+
}
|
|
25
|
+
function isLegacyFullAuto(args) {
|
|
26
|
+
return args['ask-for-approval'] === 'never'
|
|
27
|
+
&& (args.sandbox === 'workspace-write' || args.sandbox == null);
|
|
28
|
+
}
|
|
29
|
+
export function deriveCodexPermissionEnvelope(args) {
|
|
30
|
+
if (args.sandbox === 'read-only') {
|
|
31
|
+
return {
|
|
32
|
+
defaultPermissionMode: 'readonly',
|
|
33
|
+
availablePermissionModes: codexOptionsThrough('readonly'),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (args.sandbox === 'danger-full-access') {
|
|
37
|
+
return {
|
|
38
|
+
availablePermissionModes: args['dangerously-bypass-approvals-and-sandbox']
|
|
39
|
+
? [...CODEX_PERMISSION_OPTIONS]
|
|
40
|
+
: codexOptionsThrough('full-auto'),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (args['dangerously-bypass-approvals-and-sandbox']) {
|
|
44
|
+
return {
|
|
45
|
+
defaultPermissionMode: 'bypass',
|
|
46
|
+
availablePermissionModes: [...CODEX_PERMISSION_OPTIONS],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (args['full-auto'] || isLegacyFullAuto(args)) {
|
|
50
|
+
return {
|
|
51
|
+
defaultPermissionMode: 'full-auto',
|
|
52
|
+
availablePermissionModes: codexOptionsThrough('full-auto'),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
defaultPermissionMode: 'workspace',
|
|
57
|
+
availablePermissionModes: codexOptionsThrough('workspace'),
|
|
58
|
+
};
|
|
59
|
+
}
|
package/dist/register.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export declare function main(): Promise<void>;
|
package/dist/register.js
CHANGED
|
@@ -1,83 +1,90 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setDefaultResultOrder } from 'node:dns';
|
|
3
|
-
setDefaultResultOrder('ipv4first');
|
|
4
3
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
4
|
import { homedir } from 'node:os';
|
|
6
5
|
import { join } from 'node:path';
|
|
7
6
|
import { parseArgs } from 'node:util';
|
|
8
7
|
import { registerAndWaitForApproval } from '@canonmsg/core';
|
|
8
|
+
import { runCli } from './cli-entry.js';
|
|
9
9
|
const CANON_DIR = join(homedir(), '.canon');
|
|
10
10
|
const AGENTS_PATH = join(CANON_DIR, 'agents.json');
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
profiles =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
11
|
+
export async function main() {
|
|
12
|
+
setDefaultResultOrder('ipv4first');
|
|
13
|
+
const { values } = parseArgs({
|
|
14
|
+
options: {
|
|
15
|
+
name: { type: 'string' },
|
|
16
|
+
description: { type: 'string' },
|
|
17
|
+
phone: { type: 'string' },
|
|
18
|
+
profile: { type: 'string' },
|
|
19
|
+
'base-url': { type: 'string' },
|
|
20
|
+
},
|
|
21
|
+
strict: true,
|
|
22
|
+
});
|
|
23
|
+
if (!values.name || !values.description || !values.phone) {
|
|
24
|
+
console.error('Usage: canon-codex-register --name "Agent Name" --description "Description" --phone "+15551234567" [--profile "my-agent"]');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const profileName = values.profile || values.name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
|
|
28
|
+
let existingAgentId;
|
|
29
|
+
try {
|
|
30
|
+
const profiles = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
|
|
31
|
+
existingAgentId = profiles[profileName]?.agentId;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// No existing profile state.
|
|
35
|
+
}
|
|
36
|
+
console.log(`Registering Codex agent "${values.name}" (profile: ${profileName})...`);
|
|
37
|
+
const result = await registerAndWaitForApproval({
|
|
38
|
+
name: values.name,
|
|
39
|
+
description: values.description,
|
|
40
|
+
ownerPhone: values.phone,
|
|
41
|
+
developerInfo: 'Codex host plugin',
|
|
42
|
+
clientType: 'codex',
|
|
43
|
+
baseUrl: values['base-url'],
|
|
44
|
+
requestedAgentId: existingAgentId,
|
|
45
|
+
}, {
|
|
46
|
+
onSubmitted: (requestId) => {
|
|
47
|
+
console.log(`Registration submitted (request ID: ${requestId}).`);
|
|
48
|
+
console.log('Waiting for approval in Canon app...');
|
|
49
|
+
},
|
|
50
|
+
onPollUpdate: () => {
|
|
51
|
+
process.stdout.write('.');
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
console.log('');
|
|
55
|
+
switch (result.status) {
|
|
56
|
+
case 'approved': {
|
|
57
|
+
mkdirSync(CANON_DIR, { recursive: true });
|
|
58
|
+
let profiles = {};
|
|
59
|
+
try {
|
|
60
|
+
profiles = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// File does not exist yet.
|
|
64
|
+
}
|
|
65
|
+
profiles[profileName] = {
|
|
66
|
+
apiKey: result.apiKey,
|
|
67
|
+
agentId: result.agentId,
|
|
68
|
+
agentName: result.agentName,
|
|
69
|
+
registeredAt: new Date().toISOString(),
|
|
70
|
+
};
|
|
71
|
+
writeFileSync(AGENTS_PATH, JSON.stringify(profiles, null, 2));
|
|
72
|
+
console.log(`Approved! Agent: ${result.agentName} (${result.agentId})`);
|
|
73
|
+
console.log(`Saved as profile "${profileName}" in ~/.canon/agents.json`);
|
|
74
|
+
console.log('Start it with: canon-codex --cwd /path/to/project');
|
|
75
|
+
break;
|
|
62
76
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log(`Saved as profile "${profileName}" in ~/.canon/agents.json`);
|
|
72
|
-
console.log('Start it with: canon-codex --cwd /path/to/project');
|
|
73
|
-
break;
|
|
77
|
+
case 'rejected':
|
|
78
|
+
console.log('Registration was rejected.');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
break;
|
|
81
|
+
case 'timeout':
|
|
82
|
+
console.log('Registration timed out (5 minutes). Try again later.');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
break;
|
|
74
85
|
}
|
|
75
|
-
case 'rejected':
|
|
76
|
-
console.log('Registration was rejected.');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
break;
|
|
79
|
-
case 'timeout':
|
|
80
|
-
console.log('Registration timed out (5 minutes). Try again later.');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
break;
|
|
83
86
|
}
|
|
87
|
+
runCli(import.meta.url, main, (error) => {
|
|
88
|
+
console.error('[canon-codex-register] Fatal error:', error);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
});
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export declare function main(): void;
|
package/dist/setup.js
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { runCli } from './cli-entry.js';
|
|
4
|
+
export function main() {
|
|
5
|
+
console.log('Canon Codex Plugin Setup');
|
|
6
|
+
console.log('========================\n');
|
|
7
|
+
const version = spawnSync('codex', ['--version'], { encoding: 'utf-8' });
|
|
8
|
+
if (version.status === 0) {
|
|
9
|
+
console.log(`Detected Codex CLI: ${version.stdout.trim()}\n`);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
console.log('Codex CLI was not detected on PATH.');
|
|
13
|
+
console.log('Install Codex first, then rerun this setup.\n');
|
|
14
|
+
}
|
|
15
|
+
console.log('Next steps:');
|
|
16
|
+
console.log(' 0. Confirm Codex is logged in the way you want Canon to use');
|
|
17
|
+
console.log(' codex login status');
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(' 1. Register your agent');
|
|
20
|
+
console.log(' canon-codex-register --name "My Codex" --description "My local coding agent" --phone "+15551234567"');
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(' 2. Start the host in a project directory and keep it running');
|
|
23
|
+
console.log(' canon-codex --cwd /path/to/project');
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(' A git repo is not required; any readable directory works.');
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log('Optional flags:');
|
|
28
|
+
console.log(' --model gpt-5.4');
|
|
29
|
+
console.log(' --sandbox workspace-write');
|
|
30
|
+
console.log(' --full-auto');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('Note: recent Codex CLI versions use --full-auto for non-interactive write access.');
|
|
33
|
+
console.log('Note: Canon uses the local Codex login state by default (for example ChatGPT/device auth or API-key auth).');
|
|
34
|
+
console.log('Note: If Canon starts returning "Invalid API key", rerun canon-codex-register to replace the saved profile and then restart the host.');
|
|
8
35
|
}
|
|
9
|
-
|
|
10
|
-
console.
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
console.log('Next steps:');
|
|
14
|
-
console.log(' 0. Confirm Codex is logged in the way you want Canon to use');
|
|
15
|
-
console.log(' codex login status');
|
|
16
|
-
console.log('');
|
|
17
|
-
console.log(' 1. Register your agent');
|
|
18
|
-
console.log(' canon-codex-register --name "My Codex" --description "My local coding agent" --phone "+15551234567"');
|
|
19
|
-
console.log('');
|
|
20
|
-
console.log(' 2. Start the host in a project directory and keep it running');
|
|
21
|
-
console.log(' canon-codex --cwd /path/to/project');
|
|
22
|
-
console.log('');
|
|
23
|
-
console.log(' A git repo is not required; any readable directory works.');
|
|
24
|
-
console.log('');
|
|
25
|
-
console.log('Optional flags:');
|
|
26
|
-
console.log(' --model gpt-5.4');
|
|
27
|
-
console.log(' --sandbox workspace-write');
|
|
28
|
-
console.log(' --full-auto');
|
|
29
|
-
console.log('');
|
|
30
|
-
console.log('Note: recent Codex CLI versions use --full-auto for non-interactive write access.');
|
|
31
|
-
console.log('Note: Canon uses the local Codex login state by default (for example ChatGPT/device auth or API-key auth).');
|
|
32
|
-
console.log('Note: If Canon starts returning "Invalid API key", rerun canon-codex-register to replace the saved profile and then restart the host.');
|
|
36
|
+
runCli(import.meta.url, main, (error) => {
|
|
37
|
+
console.error('[canon-codex-setup] Fatal error:', error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/
|
|
7
|
-
"types": "dist/
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"bin": {
|
|
9
15
|
"canon-codex": "dist/host.js",
|
|
10
16
|
"canon-codex-register": "dist/register.js",
|
|
@@ -22,8 +28,8 @@
|
|
|
22
28
|
"prepack": "npm run build"
|
|
23
29
|
},
|
|
24
30
|
"dependencies": {
|
|
25
|
-
"@canonmsg/agent-sdk": "^0.8.
|
|
26
|
-
"@canonmsg/core": "^0.7.
|
|
31
|
+
"@canonmsg/agent-sdk": "^0.8.1",
|
|
32
|
+
"@canonmsg/core": "^0.7.1"
|
|
27
33
|
},
|
|
28
34
|
"engines": {
|
|
29
35
|
"node": ">=18.0.0"
|