@commonlyai/cli 0.1.164 → 0.1.166
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 +1 -1
- package/src/commands/agent.js +53 -2
- package/src/lib/adapters/claude.js +4 -1
- package/src/lib/adapters/codex.js +4 -1
- package/src/lib/adapters/dsh.js +4 -1
- package/src/lib/adapters/goose.js +4 -2
- package/src/lib/adapters/hermes.js +4 -1
- package/src/lib/adapters/opencode.js +4 -1
- package/src/lib/adapters/pi.js +4 -2
- package/src/lib/adapters/qwen.js +4 -1
- package/src/lib/adapters/seat-model.js +15 -0
package/package.json
CHANGED
package/src/commands/agent.js
CHANGED
|
@@ -17,6 +17,7 @@ import { homedir, tmpdir } from 'os';
|
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
18
|
import { createHash } from 'crypto';
|
|
19
19
|
|
|
20
|
+
import { requireSeatModel } from '../lib/adapters/seat-model.js';
|
|
20
21
|
import { createClient } from '../lib/api.js';
|
|
21
22
|
import { copyHandoffMemory } from '../lib/handoff-memory.js';
|
|
22
23
|
import {
|
|
@@ -666,12 +667,29 @@ export const updateAgentConfiguration = async ({
|
|
|
666
667
|
model = null,
|
|
667
668
|
effort = null,
|
|
668
669
|
envPath = null,
|
|
670
|
+
runtimeKey = false,
|
|
669
671
|
parseEnv = parseEnvironmentFile,
|
|
670
672
|
adapterRegistry = { getAdapter, listAdapterNames },
|
|
671
673
|
}) => {
|
|
672
674
|
if (!record?.agentName) {
|
|
673
675
|
throw new Error('token record is missing agentName — re-attach the agent');
|
|
674
676
|
}
|
|
677
|
+
if (runtimeKey) {
|
|
678
|
+
if (adapter || envPath) throw new Error('--adapter and --env require an owner login; run commonly login');
|
|
679
|
+
const controls = Object.fromEntries(Object.entries({ model, effort })
|
|
680
|
+
.filter(([, value]) => value !== null && value !== undefined));
|
|
681
|
+
if (!Object.keys(controls).length) throw new Error('provide --model or --effort');
|
|
682
|
+
const environment = { ...record.environment, ...controls };
|
|
683
|
+
const validation = validateEnvironmentSpec(environment);
|
|
684
|
+
if (!validation.ok) throw new Error(validation.errors.join('; '));
|
|
685
|
+
const result = await client.patch('/api/agents/runtime/configuration', {
|
|
686
|
+
agentName: record.agentName, instanceId: record.instanceId || 'default', ...controls,
|
|
687
|
+
});
|
|
688
|
+
return {
|
|
689
|
+
agentName: record.agentName, instanceId: record.instanceId || 'default', podId: result.podId,
|
|
690
|
+
changed: Object.keys(controls), environment,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
675
693
|
const instanceId = record.instanceId || 'default';
|
|
676
694
|
const runtime = {};
|
|
677
695
|
const environmentRuntime = {};
|
|
@@ -1253,6 +1271,7 @@ export const performRun = ({
|
|
|
1253
1271
|
const effectiveTurnTimeoutMs = Number.isFinite(requestedTurnTimeout) && requestedTurnTimeout > 0
|
|
1254
1272
|
? Math.min(requestedTurnTimeout, 2_147_483_647) : 15 * 60 * 1000;
|
|
1255
1273
|
const client = createClient({ instance: instanceUrl, token });
|
|
1274
|
+
const missingModelNotices = new Set();
|
|
1256
1275
|
const handoffAgent = sanitizeHandoffAgent(refusalHandoff);
|
|
1257
1276
|
if (refusalHandoff && !handoffAgent) {
|
|
1258
1277
|
log(`--refusal-handoff ignored: '${String(refusalHandoff).slice(0, 64)}' is not an agent handle`);
|
|
@@ -1677,6 +1696,30 @@ export const performRun = ({
|
|
|
1677
1696
|
? `${recycleBriefing}\n\n${promptWithSkills}`
|
|
1678
1697
|
: promptWithSkills;
|
|
1679
1698
|
|
|
1699
|
+
if (adapter.requiresModel) {
|
|
1700
|
+
try {
|
|
1701
|
+
requireSeatModel(seatEnvironment, adapter.name, true);
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
if (error.code === 'seat_model_missing' && eventPodId && !missingModelNotices.has(eventPodId)) {
|
|
1704
|
+
try {
|
|
1705
|
+
const delivery = await deliverChatReply({
|
|
1706
|
+
client,
|
|
1707
|
+
podId: eventPodId,
|
|
1708
|
+
text: "This agent has no model set; add environment.model to its configuration.",
|
|
1709
|
+
limit: chatCharLimit,
|
|
1710
|
+
maxChunks: maxChatChunks,
|
|
1711
|
+
log,
|
|
1712
|
+
});
|
|
1713
|
+
if (!delivery.skipped && !delivery.refused) missingModelNotices.add(eventPodId);
|
|
1714
|
+
} catch (noticeError) {
|
|
1715
|
+
log(`missing model notice could not be posted: ${noticeError.message}`);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
// Configuration failure is retryable: never turn a notice into an ack.
|
|
1719
|
+
throw error;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1680
1723
|
log(`[${event.type}] spawning ${adapter.name}`);
|
|
1681
1724
|
// TASK-102 part B. The CHILD gets a scoped credential; the wrapper keeps
|
|
1682
1725
|
// the seat token for its own polling, claiming and posting (`client` was
|
|
@@ -3748,13 +3791,21 @@ Use --local to find the name you'd pass to 'agent run' or 'agent detach'.
|
|
|
3748
3791
|
process.exit(1);
|
|
3749
3792
|
}
|
|
3750
3793
|
const instance = opts.instance || record.instanceUrl;
|
|
3751
|
-
const
|
|
3794
|
+
const ownerToken = getToken(instance);
|
|
3795
|
+
const runtimeKey = !opts.adapter && !opts.env && !ownerToken && Boolean(record.runtimeToken);
|
|
3796
|
+
const token = runtimeKey ? record.runtimeToken : ownerToken;
|
|
3752
3797
|
if (!token) { console.error('Not logged in. Run: commonly login'); process.exit(1); }
|
|
3753
|
-
const client = createClient({ instance: resolveInstanceUrl(instance), token });
|
|
3754
3798
|
try {
|
|
3799
|
+
const instanceUrl = resolveInstanceUrl(instance);
|
|
3800
|
+
if (runtimeKey && (!record.instanceUrl
|
|
3801
|
+
|| new URL(instanceUrl).href.replace(/\/$/, '') !== new URL(record.instanceUrl).href.replace(/\/$/, ''))) {
|
|
3802
|
+
throw new Error('Changing --instance requires an owner login for that instance; run commonly login');
|
|
3803
|
+
}
|
|
3804
|
+
const client = createClient({ instance: instanceUrl, token });
|
|
3755
3805
|
const result = await updateAgentConfiguration({
|
|
3756
3806
|
client,
|
|
3757
3807
|
record,
|
|
3808
|
+
runtimeKey,
|
|
3758
3809
|
adapter: opts.adapter,
|
|
3759
3810
|
model: opts.model,
|
|
3760
3811
|
effort: opts.effort,
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
* SIGKILL escalation is a post-v1 concern (ADR-005 §Spawning semantics).
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
+
import { requireSeatModel, seatModelRequired } from './seat-model.js';
|
|
46
47
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
47
48
|
import { createHash, randomUUID } from 'crypto';
|
|
48
49
|
import { accessSync, constants, realpathSync } from 'fs';
|
|
@@ -725,6 +726,7 @@ const prepareArgv = async (innerArgv, ctx) => {
|
|
|
725
726
|
};
|
|
726
727
|
|
|
727
728
|
export default {
|
|
729
|
+
get requiresModel() { return seatModelRequired(); },
|
|
728
730
|
name: 'claude',
|
|
729
731
|
// Identity-bearing runtime tag written into AgentInstallation.config.runtime
|
|
730
732
|
// by `commonly agent attach`. The CLI command stays `commonly agent attach
|
|
@@ -759,6 +761,7 @@ export default {
|
|
|
759
761
|
},
|
|
760
762
|
|
|
761
763
|
async spawn(prompt, rawCtx = {}) {
|
|
764
|
+
const seatModel = requireSeatModel(rawCtx.environment, 'claude');
|
|
762
765
|
// `sandbox.trust: 'internal'` is refused for new declarations and read as
|
|
763
766
|
// `public` for a record that already carries it; such a seat is confined
|
|
764
767
|
// where it can be and refuses to derive where it cannot (Wren 69585).
|
|
@@ -809,7 +812,7 @@ export default {
|
|
|
809
812
|
// session-recovery retry, or a retry runs at a different effort than the
|
|
810
813
|
// turn it replaces.
|
|
811
814
|
const modelArgs = [
|
|
812
|
-
...(
|
|
815
|
+
...(seatModel ? ['--model', seatModel] : []),
|
|
813
816
|
...(ctx.environment?.effort ? ['--effort', String(ctx.environment.effort)] : []),
|
|
814
817
|
];
|
|
815
818
|
const baseArgs = ['-p', fullPrompt, '--output-format', 'text', sessionFlag, sessionId, ...modelArgs];
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* SIGKILL escalation is a post-v1 concern (ADR-005 §Spawning semantics).
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
+
import { requireSeatModel, seatModelRequired } from './seat-model.js';
|
|
40
41
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
41
42
|
import { createHash } from 'crypto';
|
|
42
43
|
import { accessSync, constants } from 'fs';
|
|
@@ -616,6 +617,7 @@ const runCodex = ({ args, cwd, env, timeoutMs, credentials = [], spawnImpl = chi
|
|
|
616
617
|
});
|
|
617
618
|
|
|
618
619
|
export default {
|
|
620
|
+
get requiresModel() { return seatModelRequired(); },
|
|
619
621
|
name: 'codex',
|
|
620
622
|
// See claude.js for the rationale — identity-bearing runtime tag persisted
|
|
621
623
|
// to AgentInstallation.config.runtime (paired with host:'byo') so a CLI-
|
|
@@ -642,6 +644,7 @@ export default {
|
|
|
642
644
|
},
|
|
643
645
|
|
|
644
646
|
async spawn(prompt, ctx = {}) {
|
|
647
|
+
const seatModel = requireSeatModel(ctx.environment, 'codex');
|
|
645
648
|
// Passed through UNCOALESCED. `ctx.memoryLongTerm || ''` was here, and
|
|
646
649
|
// `null || ''` is `''` — which routed the unreadable case straight into
|
|
647
650
|
// the empty-memory branch and made the whole distinction unreachable from
|
|
@@ -698,7 +701,7 @@ export default {
|
|
|
698
701
|
mcpFlags: mcp.flags,
|
|
699
702
|
mcpReadDirectories: mcp.mcpReadDirectories,
|
|
700
703
|
publicSandboxMode,
|
|
701
|
-
model:
|
|
704
|
+
model: seatModel,
|
|
702
705
|
effort: ctx.environment?.effort,
|
|
703
706
|
commitAttributionEnv: ctx.commitAttributionEnv,
|
|
704
707
|
});
|
package/src/lib/adapters/dsh.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** DeepSeek Harness 0.2.1-rc.2, private seats only. Configuration is per spawn. */
|
|
2
|
+
import { requireSeatModel } from './seat-model.js';
|
|
2
3
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
3
4
|
import { createHash } from 'crypto';
|
|
4
5
|
import { existsSync } from 'fs';
|
|
@@ -218,6 +219,7 @@ export const runDsh = ({ binary, args, cwd, env, timeoutMs, credentials, spawnIm
|
|
|
218
219
|
});
|
|
219
220
|
|
|
220
221
|
export default {
|
|
222
|
+
requiresModel: true,
|
|
221
223
|
name: 'dsh', runtimeType: 'dsh', supportsProvider: true,
|
|
222
224
|
|
|
223
225
|
async validateEnvironment(environment, workspacePath) {
|
|
@@ -246,6 +248,7 @@ export default {
|
|
|
246
248
|
},
|
|
247
249
|
|
|
248
250
|
async spawn(prompt, ctx = {}) {
|
|
251
|
+
const seatModel = requireSeatModel(ctx.environment, 'dsh', true);
|
|
249
252
|
assertPrivateDsh(ctx.environment);
|
|
250
253
|
if (!ctx.environment?.provider || !ctx.environment?.model) {
|
|
251
254
|
throw new Error('dsh requires environment.provider and environment.model');
|
|
@@ -275,7 +278,7 @@ export default {
|
|
|
275
278
|
credentialFile: credential.path, instanceUrl: ctx.instanceUrl,
|
|
276
279
|
});
|
|
277
280
|
const patch = JSON.stringify(buildPatch({
|
|
278
|
-
proxyBaseURL: proxy.baseURL, model:
|
|
281
|
+
proxyBaseURL: proxy.baseURL, model: seatModel,
|
|
279
282
|
cwd: ctx.cwd, mcp, effort: ctx.environment.effort,
|
|
280
283
|
deepseekOfficial: new URL(ctx.environment.provider.baseURL).hostname === 'api.deepseek.com',
|
|
281
284
|
}), null, 2);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Goose 1.54.0 wrapper. Public seats wait for TASK-236's deny-by-default proof. */
|
|
2
|
+
import { requireSeatModel } from './seat-model.js';
|
|
2
3
|
import { spawn as childSpawn, spawnSync } from 'node:child_process';
|
|
3
4
|
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
5
|
import { tmpdir } from 'node:os';
|
|
@@ -16,7 +17,6 @@ import { adapterFailure, spawnCredentials } from '../upstream-refusal.js';
|
|
|
16
17
|
export const TESTED_GOOSE_VERSIONS = Object.freeze(['1.54.0']);
|
|
17
18
|
const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
18
19
|
const TERMINATION_GRACE_MS = 5000;
|
|
19
|
-
const DEFAULT_MODEL = 'deepseek-v4-flash';
|
|
20
20
|
const DEFAULT_PROVIDER_HOST = 'https://litellm.commonly.me';
|
|
21
21
|
const DEFAULT_PROVIDER_KEY = 'COMMONLY_LITELLM_KEY';
|
|
22
22
|
const PLACEHOLDER_RE = /\$\{(COMMONLY_[A-Z_]+)\}/g;
|
|
@@ -174,6 +174,7 @@ const runGoose = ({ binary, args, cwd, env, timeoutMs, credentials, spawnImpl =
|
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
export default {
|
|
177
|
+
requiresModel: true,
|
|
177
178
|
name: 'goose',
|
|
178
179
|
providerKeyEnv: DEFAULT_PROVIDER_KEY,
|
|
179
180
|
supportsProvider: true,
|
|
@@ -193,6 +194,7 @@ export default {
|
|
|
193
194
|
},
|
|
194
195
|
|
|
195
196
|
async spawn(prompt, ctx = {}) {
|
|
197
|
+
const seatModel = requireSeatModel(ctx.environment, 'goose', true);
|
|
196
198
|
const keyFile = await this.validateEnvironment(ctx.environment, ctx.cwd);
|
|
197
199
|
const sourceEnv = ctx.env || process.env;
|
|
198
200
|
const configured = ctx.environment?.provider;
|
|
@@ -203,7 +205,7 @@ export default {
|
|
|
203
205
|
if (!/^https?:$/.test(baseUrl.protocol) || baseUrl.username || baseUrl.password || baseUrl.search || baseUrl.hash) {
|
|
204
206
|
throw new Error('goose adapter: provider.baseURL must be a plain HTTP(S) URL');
|
|
205
207
|
}
|
|
206
|
-
const model =
|
|
208
|
+
const model = seatModel;
|
|
207
209
|
const binary = ctx._binaryPath || 'goose';
|
|
208
210
|
const version = ctx._version || versionOf(spawnSync(binary, ['--version'], { encoding: 'utf8', timeout: 5000 }).stdout);
|
|
209
211
|
if (!TESTED_GOOSE_VERSIONS.includes(version)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Hermes 0.22.2 local CLI wrapper. Hermes owns provider I/O; this file only spawns it. */
|
|
2
|
+
import { requireSeatModel, seatModelRequired } from './seat-model.js';
|
|
2
3
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
3
4
|
import { createHash } from 'crypto';
|
|
4
5
|
import { mkdir } from 'fs/promises';
|
|
@@ -137,6 +138,7 @@ const runHermes = ({ args, prompt, cwd, env, timeoutMs, spawnImpl, credentials }
|
|
|
137
138
|
});
|
|
138
139
|
|
|
139
140
|
export default {
|
|
141
|
+
get requiresModel() { return seatModelRequired(); },
|
|
140
142
|
name: 'hermes',
|
|
141
143
|
runtimeType: 'hermes',
|
|
142
144
|
supportsProvider: false,
|
|
@@ -161,6 +163,7 @@ export default {
|
|
|
161
163
|
},
|
|
162
164
|
|
|
163
165
|
async spawn(prompt, ctx = {}) {
|
|
166
|
+
const seatModel = requireSeatModel(ctx.environment, 'hermes');
|
|
164
167
|
await this.validateEnvironment(ctx.environment);
|
|
165
168
|
// Keep Hermes away from the operator's default profile, including any
|
|
166
169
|
// Nous Portal/Cloud credentials there. The wrapper provides the sole key.
|
|
@@ -177,7 +180,7 @@ export default {
|
|
|
177
180
|
}
|
|
178
181
|
const args = ['chat', '--oneshot', '--query-file', '-', '--format', 'stream-json',
|
|
179
182
|
'--provider', 'openrouter', '--source', 'tool', '--no-restore-cwd'];
|
|
180
|
-
if (
|
|
183
|
+
if (seatModel) args.push('--model', seatModel);
|
|
181
184
|
if (ctx.sessionId) args.push('--resume', ctx.sessionId);
|
|
182
185
|
const fullPrompt = buildMemoryPreamble(prompt, ctx.memoryLongTerm, {
|
|
183
186
|
freshSession: !ctx.sessionId,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* `text.part.text`, and token counts in `step_finish.part.tokens`.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { requireSeatModel, seatModelRequired } from './seat-model.js';
|
|
22
23
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
23
24
|
import { createHash } from 'crypto';
|
|
24
25
|
import { realpathSync } from 'fs';
|
|
@@ -622,6 +623,7 @@ const describeOpenCodeVersionMismatch = (installedVersion) => {
|
|
|
622
623
|
};
|
|
623
624
|
|
|
624
625
|
export default {
|
|
626
|
+
get requiresModel() { return seatModelRequired(); },
|
|
625
627
|
name: 'opencode',
|
|
626
628
|
runtimeType: 'opencode',
|
|
627
629
|
supportsProvider: true,
|
|
@@ -645,6 +647,7 @@ export default {
|
|
|
645
647
|
},
|
|
646
648
|
|
|
647
649
|
async spawn(prompt, rawCtx = {}) {
|
|
650
|
+
const seatModel = requireSeatModel(rawCtx.environment, 'opencode');
|
|
648
651
|
const sandbox = normalizeSandboxTrust(rawCtx.environment?.sandbox);
|
|
649
652
|
const ctx = rawCtx.environment
|
|
650
653
|
? { ...rawCtx, environment: { ...rawCtx.environment, sandbox } }
|
|
@@ -805,7 +808,7 @@ export default {
|
|
|
805
808
|
prompt: fullPrompt,
|
|
806
809
|
sessionId: ctx.sessionId,
|
|
807
810
|
cwd: ctx.cwd,
|
|
808
|
-
model: qualifiedProviderModel(provider,
|
|
811
|
+
model: qualifiedProviderModel(provider, seatModel),
|
|
809
812
|
effort: ctx.environment?.effort,
|
|
810
813
|
title: ctx.agentName,
|
|
811
814
|
});
|
package/src/lib/adapters/pi.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* Contract (see stub.js): detect() and spawn(prompt, ctx) → { text, newSessionId }.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
import { requireSeatModel } from './seat-model.js';
|
|
35
36
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
36
37
|
import { createHash, randomUUID } from 'crypto';
|
|
37
38
|
import { mkdir, readdir, writeFile } from 'fs/promises';
|
|
@@ -65,7 +66,6 @@ export const DEFAULT_PROVIDER = Object.freeze({
|
|
|
65
66
|
api: 'openai-completions',
|
|
66
67
|
apiKeyEnv: 'COMMONLY_LITELLM_KEY',
|
|
67
68
|
});
|
|
68
|
-
export const DEFAULT_MODEL = 'deepseek-v4-flash';
|
|
69
69
|
|
|
70
70
|
// ADR-008 `effort` → pi `--thinking`. pi's ladder is off/minimal/low/medium/high/xhigh/max.
|
|
71
71
|
const THINKING = { none: 'off', off: 'off', minimal: 'minimal', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' };
|
|
@@ -390,6 +390,7 @@ const runPi = ({ args, cwd, env, payload, timeoutMs, credentials = [], spawnImpl
|
|
|
390
390
|
});
|
|
391
391
|
|
|
392
392
|
export default {
|
|
393
|
+
requiresModel: true,
|
|
393
394
|
name: 'pi',
|
|
394
395
|
|
|
395
396
|
// TASK-049: the variable this adapter needs from the environment of whatever
|
|
@@ -409,12 +410,13 @@ export default {
|
|
|
409
410
|
},
|
|
410
411
|
|
|
411
412
|
async spawn(prompt, ctx = {}) {
|
|
413
|
+
const seatModel = requireSeatModel(ctx.environment, 'pi', true);
|
|
412
414
|
// pi has no enforced sandbox. claude.js and codex.js refuse a public-trust
|
|
413
415
|
// seat they cannot confine; so does this adapter, and it refuses any declared
|
|
414
416
|
// sandbox mode rather than run unconfined under a spec that promised one.
|
|
415
417
|
assertNoSandboxDeclared(ctx.environment);
|
|
416
418
|
const provider = resolveProvider(ctx.environment);
|
|
417
|
-
const model =
|
|
419
|
+
const model = seatModel;
|
|
418
420
|
const thinking = thinkingFor(ctx.environment?.effort);
|
|
419
421
|
const baseEnv = ctx.env || process.env;
|
|
420
422
|
if (!baseEnv[provider.apiKeyEnv]) {
|
package/src/lib/adapters/qwen.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Qwen Code 0.25.1 wrapper. Public turns have fresh HOME, config and keys. */
|
|
2
|
+
import { requireSeatModel } from './seat-model.js';
|
|
2
3
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
3
4
|
import { lstatSync, realpathSync } from 'fs';
|
|
4
5
|
import { chmod, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
|
|
@@ -208,6 +209,7 @@ const runQwen = ({ cmd, args, cwd, env, timeoutMs, spawnImpl, credentials }) =>
|
|
|
208
209
|
});
|
|
209
210
|
|
|
210
211
|
export default {
|
|
212
|
+
requiresModel: true,
|
|
211
213
|
name: 'qwen',
|
|
212
214
|
runtimeType: 'qwen',
|
|
213
215
|
supportsProvider: true,
|
|
@@ -230,6 +232,7 @@ export default {
|
|
|
230
232
|
},
|
|
231
233
|
|
|
232
234
|
async spawn(prompt, ctx = {}) {
|
|
235
|
+
const seatModel = requireSeatModel(ctx.environment, 'qwen', true);
|
|
233
236
|
const sandbox = normalizeSandboxTrust(ctx.environment?.sandbox);
|
|
234
237
|
const isPublic = sandbox?.trust === 'public';
|
|
235
238
|
const mode = isPublic ? resolvePublicSandboxMode(sandbox) : null;
|
|
@@ -304,7 +307,7 @@ export default {
|
|
|
304
307
|
freshSession: true,
|
|
305
308
|
});
|
|
306
309
|
const args = [
|
|
307
|
-
'--bare', '--safe-mode', '--auth-type', 'openai', '-m',
|
|
310
|
+
'--bare', '--safe-mode', '--auth-type', 'openai', '-m', seatModel,
|
|
308
311
|
'--mcp-config', mcpPath, '--exclude-tools', [
|
|
309
312
|
...EXCLUDED_TOOLS, ...(isPublic ? PUBLIC_READ_TOOLS : []),
|
|
310
313
|
].join(','),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Fleet runners opt in; BYO users retain their own CLI's model default. */
|
|
2
|
+
export const seatModelRequired = () => process.env.COMMONLY_REQUIRE_SEAT_MODEL === '1';
|
|
3
|
+
|
|
4
|
+
export const requireSeatModel = (environment, adapterName, required = seatModelRequired()) => {
|
|
5
|
+
const model = environment?.model;
|
|
6
|
+
if (typeof model !== 'string' || !model.trim()) {
|
|
7
|
+
if (!required) return undefined;
|
|
8
|
+
const error = new Error(
|
|
9
|
+
`${adapterName} requires environment.model; set the seat model with commonly agent config <name> --model <model> before running`,
|
|
10
|
+
);
|
|
11
|
+
error.code = 'seat_model_missing';
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
return model.trim();
|
|
15
|
+
};
|