Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions scripts/manage-api-key.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,30 @@ const args = process.argv.slice(2)
const positionalArgs = args.filter((arg) => arg !== '--connect')
const connectAfterStore = args.includes('--connect')
const command = positionalArgs[0] || 'status'
const CLONE_LOOP_SOURCE_DETAIL = 'clone-loop:claude-code'
let handled = false

function cloneLoopAgentId() {
const explicit = String(process.env.CLONE_LOOP_AGENT_ID || '').trim()
if (explicit) return explicit

if (process.env.CLAUDE_PLUGIN_DATA || process.env.CLAUDE_PLUGIN_ROOT) {
return 'claude-code'
}

const hasCodexPluginData = Boolean(process.env.PLUGIN_DATA) && !process.env.CLAUDE_PLUGIN_DATA
const hasCodexPluginRoot = Boolean(process.env.PLUGIN_ROOT) && !process.env.CLAUDE_PLUGIN_ROOT
const hasCodexRuntime =
Boolean(process.env.CODEX_HOME) ||
Boolean(process.env.CODEX_SESSION_ID) ||
Boolean(process.env.CODEX_THREAD_ID)

return hasCodexPluginData || hasCodexPluginRoot || hasCodexRuntime ? 'codex' : 'claude-code'
}

function cloneLoopSourceDetail() {
return `clone-loop:${cloneLoopAgentId()}`
}

function usage() {
console.log(`Clone API key manager

Expand Down Expand Up @@ -79,7 +100,7 @@ async function startDashboardSession(token) {
name: 'start_session',
arguments: {
source: 'agent',
source_detail: CLONE_LOOP_SOURCE_DETAIL,
source_detail: cloneLoopSourceDetail(),
},
},
init.sessionId,
Expand Down
3 changes: 3 additions & 0 deletions skills/clone-api-key/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ Supported subcommands:

- `status`
- `import-env`
- `import-env --connect`
- `set <key>`
- `set <key> --connect`
- `connect`
- `clear`

Never print the full token. Report only the masked token and active source: `environment`, `plugin config`, or `demo fallback`.
20 changes: 20 additions & 0 deletions tests/api-key-manager.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,26 @@ describe('Clone API key manager', () => {
})
})

it('pings the Codex plugin card when Codex plugin data is injected', async () => {
await withMcpServer(async (mcpUrl, calls) => {
await withPluginDataAsync(async (pluginDataDir) => {
const token = 'clone_codex_connect_token_1234567890'
const result = await runManagerAsync(['set', token, '--connect'], {
env: {
CLONE_LOOP_AGENT_ID: 'codex',
PLUGIN_DATA: pluginDataDir,
CLONE_MCP_URL: mcpUrl,
},
})

assert.equal(result.status, 0, JSON.stringify({ stdout: result.stdout, stderr: result.stderr }, null, 2))
assert.equal(calls[1].params.arguments.source_detail, 'clone-loop:codex')
assert.match(result.stdout, /Clone session: clone-session-123/)
assert.doesNotMatch(result.stdout, new RegExp(token))
})
})
})

it('clears a plugin config token', () => {
withPluginData((pluginDataDir) => {
writeFileSync(
Expand Down