@commonlyai/cli 0.1.85 → 0.1.87

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonlyai/cli",
3
- "version": "0.1.85",
3
+ "version": "0.1.87",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI — connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -127,6 +127,30 @@ export const deleteAgentToken = (name) => {
127
127
  // falls back to the attach hint).
128
128
  export const BOOTSTRAP_ADAPTER_DETECT_ORDER = ['claude', 'codex', 'pi'];
129
129
 
130
+ // ── `agent run --adapter` against an existing token file (#2098) ────────────
131
+ // The flag picks the CLI once, at first-run bootstrap; after that the token
132
+ // file's adapter is what runs. A different `--adapter` used to be dropped
133
+ // without a word, so "switch this agent to Codex" kept running Claude and read
134
+ // as Codex misbehaving. Refuse instead, and name the way out for each path:
135
+ // a foreground run re-bootstraps from the Connect page's env exports after the
136
+ // local record is removed; a daemon-managed seat is switched through the
137
+ // registry, which the daemon applies on its next pass.
138
+ export const checkRunAdapterRequest = ({ record, requestedAdapter, tokenPath }) => {
139
+ const requested = String(requestedAdapter || '').trim().toLowerCase();
140
+ if (!requested) return { ok: true };
141
+ const bound = String(record?.adapter || '').trim().toLowerCase();
142
+ if (!bound || bound === requested) return { ok: true };
143
+ const name = record.agentName;
144
+ return {
145
+ ok: false,
146
+ message: [
147
+ `'${name}' is bound to the ${bound} adapter by its token file (${tokenPath}); --adapter ${requested} was not applied.`,
148
+ `To run it with ${requested} here: commonly agent detach ${name} --force, then re-run with the Connect-page env exports and --adapter ${requested}.`,
149
+ `A daemon-managed seat switches with: commonly agent config ${name} --adapter ${requested}`,
150
+ ].join('\n'),
151
+ };
152
+ };
153
+
130
154
  export const bootstrapAgentRecordFromEnv = async ({
131
155
  name,
132
156
  env = process.env,
@@ -2690,7 +2714,7 @@ Docs:
2690
2714
  .command('run <name>')
2691
2715
  .description('Run the local-CLI wrapper loop for an attached agent')
2692
2716
  .option('--interval <ms>', 'Poll interval in ms', '5000')
2693
- .option('--adapter <name>', 'CLI to wrap on first-run bootstrap (claude|codex); ignored when a token file already exists')
2717
+ .option('--adapter <name>', 'CLI to wrap on first-run bootstrap (claude|codex); an existing token file bound to a different adapter stops the run')
2694
2718
  .option('--cascade-cap <n>', `Consecutive agent-triggered turns allowed per pod (env ${CASCADE_ENV_VARS.cap}, default ${CASCADE_DEFAULTS.cap})`)
2695
2719
  .option('--cascade-grace <n>', `Extra turns allowed when this seat was directly addressed; 0 disables the grace (env ${CASCADE_ENV_VARS.addressedGrace}, default ${CASCADE_DEFAULTS.addressedGrace})`)
2696
2720
  .option('--cascade-reset <ms>', `Silence window that clears the streak (env ${CASCADE_ENV_VARS.resetMs}, default ${CASCADE_DEFAULTS.resetMs})`)
@@ -2722,6 +2746,14 @@ Docs:
2722
2746
  }
2723
2747
  }
2724
2748
 
2749
+ const adapterRequest = checkRunAdapterRequest({
2750
+ record, requestedAdapter: opts.adapter, tokenPath: tokenFile(name),
2751
+ });
2752
+ if (!adapterRequest.ok) {
2753
+ console.error(`${stamp()} [${name}] ${adapterRequest.message}`);
2754
+ process.exit(1);
2755
+ }
2756
+
2725
2757
  const adapter = getAdapter(record.adapter);
2726
2758
  if (!adapter) {
2727
2759
  console.error(`Unknown adapter '${record.adapter}' in token file. Known: ${listAdapterNames().join(', ')}`);
@@ -109,7 +109,17 @@ GIT_CONFIG_COUNT=$base_count
109
109
  export GIT_CONFIG_COUNT
110
110
  unset "GIT_CONFIG_KEY_$config_index" "GIT_CONFIG_VALUE_$config_index"
111
111
 
112
- original_hooks=\${COMMONLY_AGENT_ORIGINAL_HOOKS_PATH:-}
112
+ # Read the target repository's file-backed config while excluding process-level
113
+ # GIT_CONFIG_* overlays inherited from the workspace seat.
114
+ if original_hooks=$(GIT_CONFIG_COUNT=0 git config --path --get core.hooksPath 2>/dev/null); then
115
+ :
116
+ else
117
+ git_common_dir=$(GIT_CONFIG_COUNT=0 git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) || git_common_dir=
118
+ original_hooks=
119
+ if [ -n "$git_common_dir" ]; then
120
+ original_hooks="$git_common_dir/hooks"
121
+ fi
122
+ fi
113
123
  if [ -n "$original_hooks" ] && [ "$original_hooks" != "$COMMONLY_AGENT_HOOKS_PATH" ]; then
114
124
  original_hook="$original_hooks/$hook_name"
115
125
  if [ -x "$original_hook" ]; then
@@ -167,6 +177,9 @@ export const prepareCommitAttribution = ({
167
177
  COMMONLY_AGENT_GIT_CONFIG_INDEX: String(configIndex),
168
178
  COMMONLY_AGENT_GIT_CONFIG_BASE_COUNT: String(configIndex),
169
179
  COMMONLY_AGENT_HOOKS_PATH: hooksDirectory,
180
+ // The sandbox adapters allow the workspace's existing hook files. The
181
+ // proxy resolves the target repo's path at hook time; a confined seat may
182
+ // fail closed if that repo's hook lies outside the workspace allowlist.
170
183
  ...(originalHooksPath ? { COMMONLY_AGENT_ORIGINAL_HOOKS_PATH: originalHooksPath } : {}),
171
184
  COMMONLY_AGENT_SEAT_NAME: cleanSingleLine(displayName || agentName, 'agent'),
172
185
  COMMONLY_AGENT_SEAT_ID: seatAddressId(agentName, instanceId),