@commonlyai/cli 0.1.94 → 0.1.96
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/lib/adapters/opencode.js +82 -11
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenCode adapter — wraps `opencode run --format json` as a Commonly seat.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* under XDG_DATA_HOME. Trusted seats use the
|
|
6
|
-
* public seats get per-identity state without
|
|
7
|
-
* environment limited to safe launch/locale
|
|
4
|
+
* Smoke-tested versions are recorded in TESTED_OPENCODE_VERSIONS below.
|
|
5
|
+
* OpenCode stores sessions under XDG_DATA_HOME. Trusted seats use the
|
|
6
|
+
* operator's data home directly; public seats get per-identity state without
|
|
7
|
+
* operator auth.json and a child environment limited to safe launch/locale
|
|
8
|
+
* variables. Public provider keys
|
|
8
9
|
* stay in the host adapter and reach the configured provider through a
|
|
9
10
|
* per-spawn loopback proxy; OpenCode receives only that proxy's bearer.
|
|
10
11
|
* This protects the key, not provider spend: the model can use its seat's key
|
|
@@ -56,6 +57,10 @@ import { resolvePublicSandboxMode } from '../sandbox/mode.js';
|
|
|
56
57
|
|
|
57
58
|
const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
58
59
|
const TERMINATION_GRACE_MS = 5000;
|
|
60
|
+
// Bump only with a passing real sandbox-exec smoke against the new version.
|
|
61
|
+
const TESTED_OPENCODE_VERSIONS = Object.freeze(['1.18.35', '1.19.0']);
|
|
62
|
+
const ALLOW_UNTESTED_OPENCODE_VERSION_ENV = 'COMMONLY_OPENCODE_ALLOW_UNTESTED_VERSION';
|
|
63
|
+
const VERSION_CHECK_TIMEOUT_MS = 5000;
|
|
59
64
|
const OPENCODE_CONFIG_GITIGNORE = [
|
|
60
65
|
'node_modules',
|
|
61
66
|
'package.json',
|
|
@@ -713,6 +718,52 @@ const spawnBinaryPath = (env) => {
|
|
|
713
718
|
return 'opencode';
|
|
714
719
|
};
|
|
715
720
|
|
|
721
|
+
const parseOpenCodeVersion = (output) => {
|
|
722
|
+
const match = String(output || '').trim().match(
|
|
723
|
+
/^(?:opencode\s+)?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/i,
|
|
724
|
+
);
|
|
725
|
+
return match?.[1] || null;
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
const readOpenCodeVersion = (binary, env) => {
|
|
729
|
+
try {
|
|
730
|
+
const probeEnv = { ...env };
|
|
731
|
+
for (const key of DISABLED_ENV) probeEnv[key] = '1';
|
|
732
|
+
const result = spawnSync(binary, ['--version'], {
|
|
733
|
+
encoding: 'utf8', env: probeEnv, timeout: VERSION_CHECK_TIMEOUT_MS,
|
|
734
|
+
});
|
|
735
|
+
if (result.error || result.status !== 0) return null;
|
|
736
|
+
return parseOpenCodeVersion(result.stdout);
|
|
737
|
+
} catch {
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
const compareOpenCodeVersions = (left, right) => {
|
|
743
|
+
const leftParts = left.split(/[+-]/, 1)[0].split('.').map(Number);
|
|
744
|
+
const rightParts = right.split(/[+-]/, 1)[0].split('.').map(Number);
|
|
745
|
+
for (let index = 0; index < 3; index += 1) {
|
|
746
|
+
if (leftParts[index] !== rightParts[index]) {
|
|
747
|
+
return leftParts[index] > rightParts[index] ? 1 : -1;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return 0;
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
const describeOpenCodeVersionMismatch = (installedVersion) => {
|
|
754
|
+
const tested = TESTED_OPENCODE_VERSIONS.join(', ');
|
|
755
|
+
if (!installedVersion) return `installed OpenCode version is unknown; smoke-tested versions: ${tested}`;
|
|
756
|
+
const oldest = TESTED_OPENCODE_VERSIONS[0];
|
|
757
|
+
const newest = TESTED_OPENCODE_VERSIONS[TESTED_OPENCODE_VERSIONS.length - 1];
|
|
758
|
+
if (compareOpenCodeVersions(installedVersion, newest) > 0) {
|
|
759
|
+
return `installed OpenCode ${installedVersion} is newer than smoke-tested versions ${tested}`;
|
|
760
|
+
}
|
|
761
|
+
if (compareOpenCodeVersions(installedVersion, oldest) < 0) {
|
|
762
|
+
return `installed OpenCode ${installedVersion} is older than smoke-tested versions ${tested}`;
|
|
763
|
+
}
|
|
764
|
+
return `installed OpenCode ${installedVersion} differs from smoke-tested versions ${tested}`;
|
|
765
|
+
};
|
|
766
|
+
|
|
716
767
|
export default {
|
|
717
768
|
name: 'opencode',
|
|
718
769
|
runtimeType: 'opencode',
|
|
@@ -726,8 +777,7 @@ export default {
|
|
|
726
777
|
const res = spawnSync('opencode', ['--version'], { encoding: 'utf8' });
|
|
727
778
|
if (res.error || res.status !== 0) return null;
|
|
728
779
|
const stdout = (res.stdout || '').trim();
|
|
729
|
-
const
|
|
730
|
-
const version = versionMatch ? versionMatch[1] : (stdout || 'unknown');
|
|
780
|
+
const version = parseOpenCodeVersion(stdout) || stdout || 'unknown';
|
|
731
781
|
const where = spawnSync('which', ['opencode'], { encoding: 'utf8' });
|
|
732
782
|
const path = where.status === 0 ? (where.stdout || '').trim() || 'opencode' : 'opencode';
|
|
733
783
|
return { path, version };
|
|
@@ -761,6 +811,29 @@ export default {
|
|
|
761
811
|
}
|
|
762
812
|
const providerKeyFile = await validateProviderEnvironment(ctx.environment, ctx.cwd);
|
|
763
813
|
|
|
814
|
+
const sourceEnv = ctx.env || process.env;
|
|
815
|
+
const childSourceEnv = Object.fromEntries(Object.entries(sourceEnv)
|
|
816
|
+
.filter(([key]) => key !== ALLOW_UNTESTED_OPENCODE_VERSION_ENV));
|
|
817
|
+
const runtimeEnv = isPublic
|
|
818
|
+
? Object.fromEntries(Object.entries(childSourceEnv).filter(([key]) => PUBLIC_SAFE_ENV.test(key)))
|
|
819
|
+
: childSourceEnv;
|
|
820
|
+
const binary = ctx._binaryPath || spawnBinaryPath(runtimeEnv);
|
|
821
|
+
const installedVersion = readOpenCodeVersion(binary, runtimeEnv);
|
|
822
|
+
if (!TESTED_OPENCODE_VERSIONS.includes(installedVersion)) {
|
|
823
|
+
const mismatch = describeOpenCodeVersionMismatch(installedVersion);
|
|
824
|
+
const allowOverride = process.env[ALLOW_UNTESTED_OPENCODE_VERSION_ENV] === '1';
|
|
825
|
+
if (isPublic && !allowOverride) {
|
|
826
|
+
throw new Error(
|
|
827
|
+
`Public OpenCode seats refuse to run: ${mismatch}. `
|
|
828
|
+
+ `To override from the host process, set ${ALLOW_UNTESTED_OPENCODE_VERSION_ENV}=1.`,
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
// eslint-disable-next-line no-console
|
|
832
|
+
console.warn(isPublic
|
|
833
|
+
? `[opencode] ${mismatch}; continuing because the host process set ${ALLOW_UNTESTED_OPENCODE_VERSION_ENV}=1`
|
|
834
|
+
: `[opencode] ${mismatch}; trusted seat continues`);
|
|
835
|
+
}
|
|
836
|
+
|
|
764
837
|
const fullPrompt = buildMemoryPreamble(prompt, ctx.memoryLongTerm, {
|
|
765
838
|
freshSession: !ctx.sessionId,
|
|
766
839
|
});
|
|
@@ -834,10 +907,6 @@ export default {
|
|
|
834
907
|
});
|
|
835
908
|
await chmod(configPath, 0o600);
|
|
836
909
|
|
|
837
|
-
const sourceEnv = ctx.env || process.env;
|
|
838
|
-
const runtimeEnv = isPublic
|
|
839
|
-
? Object.fromEntries(Object.entries(sourceEnv).filter(([key]) => PUBLIC_SAFE_ENV.test(key)))
|
|
840
|
-
: sourceEnv;
|
|
841
910
|
const childEnv = makeChildEnvironment({
|
|
842
911
|
source: runtimeEnv,
|
|
843
912
|
tempDir,
|
|
@@ -858,7 +927,6 @@ export default {
|
|
|
858
927
|
effort: ctx.environment?.effort,
|
|
859
928
|
title: ctx.agentName,
|
|
860
929
|
});
|
|
861
|
-
const binary = ctx._binaryPath || spawnBinaryPath(childEnv);
|
|
862
930
|
const platform = ctx._platform || process.platform;
|
|
863
931
|
let cmd = binary;
|
|
864
932
|
let spawnArgs = args;
|
|
@@ -928,13 +996,16 @@ export default {
|
|
|
928
996
|
};
|
|
929
997
|
|
|
930
998
|
export {
|
|
999
|
+
ALLOW_UNTESTED_OPENCODE_VERSION_ENV,
|
|
931
1000
|
DISABLED_ENV,
|
|
1001
|
+
TESTED_OPENCODE_VERSIONS,
|
|
932
1002
|
buildArgs,
|
|
933
1003
|
buildMcpConfig,
|
|
934
1004
|
buildProviderConfig,
|
|
935
1005
|
makeEventParser,
|
|
936
1006
|
prepareOpenCodeDataHome,
|
|
937
1007
|
publicPermissions,
|
|
1008
|
+
readOpenCodeVersion,
|
|
938
1009
|
qualifiedProviderModel,
|
|
939
1010
|
resolveProviderKeyFile,
|
|
940
1011
|
startProviderProxy,
|