@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.
@@ -0,0 +1,2 @@
1
+ export declare function isDirectExecution(moduleUrl: string): boolean;
2
+ export declare function runCli(moduleUrl: string, main: () => void | Promise<void>, onError: (error: unknown) => void): void;
@@ -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
+ }
@@ -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;
@@ -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
- if (attachments.length > 0) {
53
- for (let i = 0; i < attachments.length; i += 1) {
54
- const att = attachments[i];
55
- const mat = materialized?.find((m) => m.index === i) ?? null;
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({ conversationId, agentId });
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: (typeof args.sandbox === 'string' ? args.sandbox : null),
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: Boolean(args['full-auto']),
285
- bypassApprovalsAndSandbox: Boolean(args['dangerously-bypass-approvals-and-sandbox']),
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)}] permissionMode control is not mapped yet (${control.permissionMode})`);
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
- main().catch((error) => {
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)
@@ -0,0 +1,3 @@
1
+ export { main as hostMain } from './host.js';
2
+ export { main as registerMain } from './register.js';
3
+ export { main as setupMain } from './setup.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { main as hostMain } from './host.js';
2
+ export { main as registerMain } from './register.js';
3
+ export { main as setupMain } from './setup.js';
@@ -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
+ }
@@ -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
- const { values } = parseArgs({
12
- options: {
13
- name: { type: 'string' },
14
- description: { type: 'string' },
15
- phone: { type: 'string' },
16
- profile: { type: 'string' },
17
- 'base-url': { type: 'string' },
18
- },
19
- strict: true,
20
- });
21
- if (!values.name || !values.description || !values.phone) {
22
- console.error('Usage: canon-codex-register --name "Agent Name" --description "Description" --phone "+15551234567" [--profile "my-agent"]');
23
- process.exit(1);
24
- }
25
- const profileName = values.profile || values.name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
26
- let existingAgentId;
27
- try {
28
- const profiles = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
29
- existingAgentId = profiles[profileName]?.agentId;
30
- }
31
- catch {
32
- // No existing profile state.
33
- }
34
- console.log(`Registering Codex agent "${values.name}" (profile: ${profileName})...`);
35
- const result = await registerAndWaitForApproval({
36
- name: values.name,
37
- description: values.description,
38
- ownerPhone: values.phone,
39
- developerInfo: 'Codex host plugin',
40
- clientType: 'codex',
41
- baseUrl: values['base-url'],
42
- requestedAgentId: existingAgentId,
43
- }, {
44
- onSubmitted: (requestId) => {
45
- console.log(`Registration submitted (request ID: ${requestId}).`);
46
- console.log('Waiting for approval in Canon app...');
47
- },
48
- onPollUpdate: () => {
49
- process.stdout.write('.');
50
- },
51
- });
52
- console.log('');
53
- switch (result.status) {
54
- case 'approved': {
55
- mkdirSync(CANON_DIR, { recursive: true });
56
- let profiles = {};
57
- try {
58
- profiles = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
59
- }
60
- catch {
61
- // File does not exist yet.
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
- profiles[profileName] = {
64
- apiKey: result.apiKey,
65
- agentId: result.agentId,
66
- agentName: result.agentName,
67
- registeredAt: new Date().toISOString(),
68
- };
69
- writeFileSync(AGENTS_PATH, JSON.stringify(profiles, null, 2));
70
- console.log(`Approved! Agent: ${result.agentName} (${result.agentId})`);
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
- console.log('Canon Codex Plugin Setup');
4
- console.log('========================\n');
5
- const version = spawnSync('codex', ['--version'], { encoding: 'utf-8' });
6
- if (version.status === 0) {
7
- console.log(`Detected Codex CLI: ${version.stdout.trim()}\n`);
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
- else {
10
- console.log('Codex CLI was not detected on PATH.');
11
- console.log('Install Codex first, then rerun this setup.\n');
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.0",
3
+ "version": "0.6.2",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
- "main": "dist/host.js",
7
- "types": "dist/host.d.ts",
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.0",
26
- "@canonmsg/core": "^0.7.0"
31
+ "@canonmsg/agent-sdk": "^0.8.1",
32
+ "@canonmsg/core": "^0.7.1"
27
33
  },
28
34
  "engines": {
29
35
  "node": ">=18.0.0"