← Files Dell Agentic WorkspaceARCHIVED FILE
scripts/configure_devspaces.py
6.14 KB · Nov 14, 2026 · 01:37 UTC
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 Dell Inc. or its subsidiaries. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Write or inspect the user-only Agentic Workspace settings; standard library only.
Settings live in ~/.config/dell-mcp/devspaces.json (or DEVSPACES_CONFIG_FILE) because Codex does
not forward launch-environment variables to plugin servers. The server reads them once at start,
so restart the plugin (start a new Codex session) after a change.
configure_devspaces.py --cluster-url https://devspaces.example.com
configure_devspaces.py --ca-bundle /path/org-ca.pem # private CA, verification on
configure_devspaces.py --fetch-cluster-certificate # self-signed cluster: pin its cert
configure_devspaces.py --allow-insecure-tls # lab only, verification off
configure_devspaces.py --no-allow-insecure-tls # turn verification back on
configure_devspaces.py --check
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import ssl
import sys
import tempfile
from urllib.parse import urlsplit
sys.path.insert(0, str(Path(__file__).resolve().parent))
import devspaces_settings # noqa: E402
def read(path):
return json.loads(path.read_text(encoding='utf-8')) if path.is_file() else {}
def write(path, values):
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
# mkstemp creates the file user-only, so a setting is never briefly readable by others.
descriptor, temporary = tempfile.mkstemp(dir=path.parent, prefix='.devspaces-', suffix='.tmp')
try:
with os.fdopen(descriptor, 'w', encoding='utf-8') as handle:
handle.write(json.dumps(values, indent=2) + '\n')
os.replace(temporary, path)
except BaseException:
Path(temporary).unlink(missing_ok=True)
raise
def fetch_certificate(cluster_url, directory):
"""Save the certificate the cluster presents and return (path, SHA-256 fingerprint).
Trust on first use: the fingerprint must be checked with the cluster administrator before the
saved certificate is relied on. This works for a self-signed cluster certificate; for a
certificate issued by a private CA, use that CA's certificate with --ca-bundle instead.
"""
parsed = urlsplit(cluster_url)
if parsed.scheme != 'https' or not parsed.hostname:
raise ValueError('cluster_url must be an https origin')
pem = ssl.get_server_certificate((parsed.hostname, parsed.port or 443), timeout=30)
fingerprint = hashlib.sha256(ssl.PEM_cert_to_DER_cert(pem)).hexdigest().upper()
directory.mkdir(parents=True, exist_ok=True, mode=0o700)
target = directory / 'devspaces-cluster.pem'
target.write_text(pem, encoding='utf-8')
if os.name == 'posix':
target.chmod(0o600)
return target, ':'.join(fingerprint[i:i + 2] for i in range(0, len(fingerprint), 2))
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--cluster-url')
parser.add_argument('--connector', help='absolute path to the approved connector executable')
parser.add_argument('--ca-bundle', help='PEM file to trust in addition to the system store')
parser.add_argument('--fetch-cluster-certificate', action='store_true')
insecure = parser.add_mutually_exclusive_group()
insecure.add_argument('--allow-insecure-tls', action='store_true', help='lab only: disable verification')
insecure.add_argument('--no-allow-insecure-tls', action='store_true')
parser.add_argument('--check', action='store_true')
args = parser.parse_args(argv)
path = devspaces_settings.config_path()
values = read(path)
if args.check:
effective = devspaces_settings.load()
print(json.dumps({
'config_path': str(path),
'cluster_url': effective.get('DEVSPACES_CLUSTER_URL'),
'connector_command': effective.get('DEVSPACES_CONNECTOR_COMMAND'),
'ca_bundle': effective.get('NODE_EXTRA_CA_CERTS'),
'tls_verification_enabled': not devspaces_settings.insecure_tls_allowed(effective),
}, indent=2))
return 0
if args.cluster_url:
if not devspaces_settings.cluster_origin(args.cluster_url):
parser.error('--cluster-url must be a credential-free HTTPS origin')
if devspaces_settings.cluster_origin(args.cluster_url) != devspaces_settings.cluster_origin(values.get('cluster_url', '')):
values.pop('allow_insecure_tls', None)
values.pop('insecure_tls_cluster', None)
values['cluster_url'] = args.cluster_url.rstrip('/')
if args.connector:
values['connector_command'] = args.connector
if args.ca_bundle:
bundle = Path(args.ca_bundle).expanduser().resolve()
ssl.PEM_cert_to_DER_cert(bundle.read_text(encoding='utf-8')) # reject a file that is not PEM
values['ca_bundle'] = str(bundle)
if args.fetch_cluster_certificate:
if not values.get('cluster_url'):
parser.error('set --cluster-url first')
target, fingerprint = fetch_certificate(values['cluster_url'], path.parent)
values['ca_bundle'] = str(target)
print(f'Saved the cluster certificate to {target}.')
print(f'SHA-256 fingerprint: {fingerprint}')
print('Confirm this fingerprint with the cluster administrator before relying on it.')
if args.allow_insecure_tls:
if not devspaces_settings.cluster_origin(values.get('cluster_url', '')):
parser.error('set --cluster-url before approving a TLS exception')
values['allow_insecure_tls'] = True
values['insecure_tls_cluster'] = values['cluster_url']
print('WARNING: certificate verification is disabled only for this approved lab cluster. '
'Use --no-allow-insecure-tls to revoke the exception.')
if args.no_allow_insecure_tls:
values.pop('allow_insecure_tls', None)
values.pop('insecure_tls_cluster', None)
write(path, values)
print(f'Wrote {path}. Restart the plugin (start a new Codex session) to apply it.')
return 0
if __name__ == '__main__':
raise SystemExit(main())
SHA-256: 7e61fab43b8c4bd20df0aa320d553045e118a710f102821a4c8553ae5d2a5f7c