mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Windows: Refactor Shell Scripts to Node.js for Cross-Platform Compatibility (#784)
This commit is contained in:
54
scripts/build.js
Normal file
54
scripts/build.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const root = join(import.meta.dirname, '..');
|
||||
|
||||
// npm install if node_modules was removed (e.g. via npm run clean or scripts/clean.js)
|
||||
if (!existsSync(join(root, 'node_modules'))) {
|
||||
execSync('npm install', { stdio: 'inherit', cwd: root });
|
||||
}
|
||||
|
||||
// build all workspaces/packages
|
||||
execSync('npm run generate', { stdio: 'inherit', cwd: root });
|
||||
execSync('npm run build --workspaces', { stdio: 'inherit', cwd: root });
|
||||
|
||||
// also build container image if sandboxing is enabled
|
||||
// skip (-s) npm install + build since we did that above
|
||||
try {
|
||||
execSync('node scripts/sandbox_command.js -q', {
|
||||
stdio: 'inherit',
|
||||
cwd: root,
|
||||
});
|
||||
if (
|
||||
process.env.BUILD_SANDBOX === '1' ||
|
||||
process.env.BUILD_SANDBOX === 'true'
|
||||
) {
|
||||
execSync('node scripts/build_sandbox.js -s', {
|
||||
stdio: 'inherit',
|
||||
cwd: root,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# npm install if node_modules was removed (e.g. via npm run clean or scripts/clean.sh)
|
||||
if [ ! -d "node_modules" ]; then
|
||||
npm install
|
||||
fi
|
||||
|
||||
# build all workspaces/packages
|
||||
npm run build --workspaces
|
||||
|
||||
# also build container image if sandboxing is enabled
|
||||
# skip (-s) npm install + build since we did that above
|
||||
if scripts/sandbox_command.sh -q && [[ "${BUILD_SANDBOX:-}" =~ ^(1|true)$ ]]; then
|
||||
scripts/build_sandbox.sh -s
|
||||
fi
|
||||
37
scripts/build_package.js
Normal file
37
scripts/build_package.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
if (!process.cwd().includes('packages')) {
|
||||
console.error('must be invoked from a package directory');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// build typescript files
|
||||
execSync('tsc --build', { stdio: 'inherit' });
|
||||
|
||||
// copy .{md,json} files
|
||||
execSync('node ../../scripts/copy_files.js', { stdio: 'inherit' });
|
||||
|
||||
// touch dist/.last_build
|
||||
writeFileSync(join(process.cwd(), 'dist', '.last_build'), '');
|
||||
process.exit(0);
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $(pwd) != *"/packages/"* ]]; then
|
||||
echo "must be invoked from a package directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# clean dist directory
|
||||
# rm -rf dist/*
|
||||
|
||||
# build typescript files
|
||||
tsc --build
|
||||
|
||||
# copy .{md,json} files
|
||||
node ../../scripts/copy_files.js
|
||||
|
||||
# touch dist/.last_build
|
||||
touch dist/.last_build
|
||||
125
scripts/build_sandbox.js
Normal file
125
scripts/build_sandbox.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { chmodSync, readFileSync, rmSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
const argv = yargs(hideBin(process.argv))
|
||||
.option('s', {
|
||||
alias: 'skip-npm-install-build',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'skip npm install + npm run build',
|
||||
})
|
||||
.option('f', {
|
||||
alias: 'dockerfile',
|
||||
type: 'string',
|
||||
description: 'use <dockerfile> for custom image',
|
||||
})
|
||||
.option('i', {
|
||||
alias: 'image',
|
||||
type: 'string',
|
||||
description: 'use <image> name for custom image',
|
||||
}).argv;
|
||||
|
||||
let sandboxCommand;
|
||||
try {
|
||||
sandboxCommand = execSync('node scripts/sandbox_command.js')
|
||||
.toString()
|
||||
.trim();
|
||||
} catch {
|
||||
console.warn(
|
||||
'WARNING: container-based sandboxing is disabled (see README.md#sandboxing)',
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (sandboxCommand === 'sandbox-exec') {
|
||||
console.warn(
|
||||
'WARNING: container-based sandboxing is disabled (see README.md#sandboxing)',
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`using ${sandboxCommand} for sandboxing`);
|
||||
|
||||
const baseImage = 'gemini-cli-sandbox';
|
||||
const customImage = argv.i;
|
||||
const baseDockerfile = 'Dockerfile';
|
||||
const customDockerfile = argv.f;
|
||||
|
||||
if (!argv.s) {
|
||||
execSync('npm install', { stdio: 'inherit' });
|
||||
execSync('npm run build --workspaces', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
console.log('packing @gemini-cli/cli ...');
|
||||
const cliPackageDir = join('packages', 'cli');
|
||||
rmSync(join(cliPackageDir, 'dist', 'gemini-cli-cli-*.tgz'), { force: true });
|
||||
execSync(`npm pack -w @gemini-cli/cli --pack-destination ./packages/cli/dist`, {
|
||||
stdio: 'ignore',
|
||||
});
|
||||
|
||||
console.log('packing @gemini-cli/core ...');
|
||||
const corePackageDir = join('packages', 'core');
|
||||
rmSync(join(corePackageDir, 'dist', 'gemini-cli-core-*.tgz'), { force: true });
|
||||
execSync(
|
||||
`npm pack -w @gemini-cli/core --pack-destination ./packages/core/dist`,
|
||||
{ stdio: 'ignore' },
|
||||
);
|
||||
|
||||
const packageVersion = JSON.parse(
|
||||
readFileSync(join(process.cwd(), 'package.json'), 'utf-8'),
|
||||
).version;
|
||||
|
||||
chmodSync(
|
||||
join(cliPackageDir, 'dist', `gemini-cli-cli-${packageVersion}.tgz`),
|
||||
0o755,
|
||||
);
|
||||
chmodSync(
|
||||
join(corePackageDir, 'dist', `gemini-cli-core-${packageVersion}.tgz`),
|
||||
0o755,
|
||||
);
|
||||
|
||||
const buildStdout = process.env.VERBOSE ? 'inherit' : 'ignore';
|
||||
|
||||
function buildImage(imageName, dockerfile) {
|
||||
console.log(`building ${imageName} ... (can be slow first time)`);
|
||||
const buildCommand =
|
||||
sandboxCommand === 'podman'
|
||||
? `${sandboxCommand} build --authfile=<(echo '{}')`
|
||||
: `${sandboxCommand} --config=".docker" buildx build`;
|
||||
|
||||
execSync(
|
||||
`${buildCommand} ${process.env.BUILD_SANDBOX_FLAGS || ''} -f "${dockerfile}" -t "${imageName}" .`,
|
||||
{ stdio: buildStdout },
|
||||
);
|
||||
console.log(`built ${imageName}`);
|
||||
}
|
||||
|
||||
buildImage(baseImage, baseDockerfile);
|
||||
|
||||
if (customDockerfile && customImage) {
|
||||
buildImage(customImage, customDockerfile);
|
||||
}
|
||||
|
||||
execSync(`${sandboxCommand} image prune -f`, { stdio: 'ignore' });
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# exit with warning if container-based sandboxing is disabled
|
||||
# note this includes the case where sandbox-exec (seatbelt) is used
|
||||
# this happens most commonly when user runs `npm run build:all` without enabling sandboxing
|
||||
if ! scripts/sandbox_command.sh -q || [ "$(scripts/sandbox_command.sh)" == "sandbox-exec" ]; then
|
||||
echo "WARNING: container-based sandboxing is disabled (see CONTRIBUTING.md#enabling-sandboxing)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CMD=$(scripts/sandbox_command.sh)
|
||||
echo "using $CMD for sandboxing"
|
||||
|
||||
BASE_IMAGE=gemini-cli-sandbox
|
||||
CUSTOM_IMAGE=''
|
||||
BASE_DOCKERFILE=Dockerfile
|
||||
CUSTOM_DOCKERFILE=''
|
||||
|
||||
SKIP_NPM_INSTALL_BUILD=false
|
||||
while getopts "sf:i:" opt; do
|
||||
case ${opt} in
|
||||
s) SKIP_NPM_INSTALL_BUILD=true ;;
|
||||
f)
|
||||
CUSTOM_DOCKERFILE=$OPTARG
|
||||
;;
|
||||
i)
|
||||
CUSTOM_IMAGE=$OPTARG
|
||||
;;
|
||||
\?)
|
||||
echo "usage: $(basename "$0") [-s] [-f <dockerfile>]"
|
||||
echo " -s: skip npm install + npm run build"
|
||||
echo " -f <dockerfile>: use <dockerfile> for custom image"
|
||||
echo " -i <image>: use <image> name for custom image"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
# npm install + npm run build unless skipping via -s option
|
||||
if [ "$SKIP_NPM_INSTALL_BUILD" = false ]; then
|
||||
npm install
|
||||
npm run build --workspaces
|
||||
fi
|
||||
|
||||
# prepare global installation files for prod builds
|
||||
# pack cli
|
||||
echo "packing @gemini-cli/cli ..."
|
||||
rm -f packages/cli/dist/gemini-cli-cli-*.tgz
|
||||
npm pack -w @gemini-cli/cli --pack-destination ./packages/cli/dist &>/dev/null
|
||||
# pack core
|
||||
echo "packing @gemini-cli/core ..."
|
||||
rm -f packages/core/dist/gemini-cli-core-*.tgz
|
||||
npm pack -w @gemini-cli/core --pack-destination ./packages/core/dist &>/dev/null
|
||||
# give node user (used during installation, see Dockerfile) access to these files
|
||||
chmod 755 packages/*/dist/gemini-cli-*.tgz
|
||||
|
||||
# redirect build output to /dev/null unless VERBOSE is set
|
||||
BUILD_STDOUT="/dev/null"
|
||||
if [ -n "${VERBOSE:-}" ]; then
|
||||
BUILD_STDOUT="/dev/stdout"
|
||||
fi
|
||||
|
||||
build_image() {
|
||||
if [[ "$CMD" == "podman" ]]; then
|
||||
# use empty --authfile to skip unnecessary auth refresh overhead
|
||||
$CMD build --authfile=<(echo '{}') "$@" >$BUILD_STDOUT
|
||||
elif [[ "$CMD" == "docker" ]]; then
|
||||
$CMD --config=".docker" buildx build "$@" >$BUILD_STDOUT
|
||||
else
|
||||
$CMD build "$@" >$BUILD_STDOUT
|
||||
fi
|
||||
}
|
||||
|
||||
echo "building $BASE_IMAGE ... (can be slow first time)"
|
||||
# shellcheck disable=SC2086 # allow globbing and word splitting for BUILD_SANDBOX_FLAGS
|
||||
build_image ${BUILD_SANDBOX_FLAGS:-} -f "$BASE_DOCKERFILE" -t "$BASE_IMAGE" .
|
||||
echo "built $BASE_IMAGE"
|
||||
|
||||
if [[ -n "$CUSTOM_DOCKERFILE" && -n "$CUSTOM_IMAGE" ]]; then
|
||||
echo "building $CUSTOM_IMAGE ... (can be slow first time)"
|
||||
# shellcheck disable=SC2086 # allow globbing and word splitting for BUILD_SANDBOX_FLAGS
|
||||
build_image ${BUILD_SANDBOX_FLAGS:-} -f "$CUSTOM_DOCKERFILE" -t "$CUSTOM_IMAGE" .
|
||||
echo "built $CUSTOM_IMAGE"
|
||||
fi
|
||||
|
||||
$CMD image prune -f >/dev/null
|
||||
32
scripts/clean.js
Normal file
32
scripts/clean.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { rmSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const root = join(import.meta.dirname, '..');
|
||||
|
||||
// remove npm install/build artifacts
|
||||
rmSync(join(root, 'node_modules'), { recursive: true, force: true });
|
||||
rmSync(join(root, 'packages/cli/src/generated/'), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
execSync('npm run clean --workspaces', { stdio: 'inherit', cwd: root });
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# remove npm install/build artifacts
|
||||
rm -rf node_modules
|
||||
rm -rf packages/cli/src/generated/
|
||||
npm run clean --workspaces
|
||||
48
scripts/copy_bundle_assets.js
Normal file
48
scripts/copy_bundle_assets.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { copyFileSync, existsSync, mkdirSync } from 'fs';
|
||||
import { join, basename } from 'path';
|
||||
import { glob } from 'glob';
|
||||
|
||||
const root = join(import.meta.dirname, '..');
|
||||
const bundleDir = join(root, 'bundle');
|
||||
|
||||
// Create the bundle directory if it doesn't exist
|
||||
if (!existsSync(bundleDir)) {
|
||||
mkdirSync(bundleDir);
|
||||
}
|
||||
|
||||
// Copy specific shell files to the root of the bundle directory
|
||||
copyFileSync(
|
||||
join(root, 'packages/core/src/tools/shell.md'),
|
||||
join(bundleDir, 'shell.md'),
|
||||
);
|
||||
copyFileSync(
|
||||
join(root, 'packages/core/src/tools/shell.json'),
|
||||
join(bundleDir, 'shell.json'),
|
||||
);
|
||||
|
||||
// Find and copy all .sb files from packages to the root of the bundle directory
|
||||
const sbFiles = glob.sync('packages/**/*.sb', { cwd: root });
|
||||
for (const file of sbFiles) {
|
||||
copyFileSync(join(root, file), join(bundleDir, basename(file)));
|
||||
}
|
||||
|
||||
console.log('Assets copied to bundle/');
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create the bundle directory if it doesn't exist
|
||||
mkdir -p bundle
|
||||
|
||||
# Copy specific shell files to the root of the bundle directory
|
||||
cp "packages/core/src/tools/shell.md" "bundle/shell.md"
|
||||
cp "packages/core/src/tools/shell.json" "bundle/shell.json"
|
||||
|
||||
# Find and copy all .sb files from packages to the root of the bundle directory
|
||||
find packages -name '*.sb' -exec cp -f {} bundle/ \;
|
||||
|
||||
echo "Assets copied to bundle/"
|
||||
61
scripts/generate-git-commit-info.js
Normal file
61
scripts/generate-git-commit-info.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const root = join(import.meta.dirname, '..');
|
||||
const generatedDir = join(root, 'packages/cli/src/generated');
|
||||
const gitCommitFile = join(generatedDir, 'git-commit.ts');
|
||||
let gitCommitInfo = 'N/A';
|
||||
|
||||
if (!existsSync(generatedDir)) {
|
||||
mkdirSync(generatedDir, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const gitHash = execSync('git rev-parse --short HEAD', {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
if (gitHash) {
|
||||
gitCommitInfo = gitHash;
|
||||
const gitStatus = execSync('git status --porcelain', {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
if (gitStatus) {
|
||||
gitCommitInfo = `${gitHash} (local modifications)`;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
const fileContent = `/**
|
||||
* @license
|
||||
* Copyright ${new Date().getFullYear()} Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// This file is auto-generated by the build script (scripts/build.js)
|
||||
// Do not edit this file manually.
|
||||
export const GIT_COMMIT_INFO = '${gitCommitInfo}';
|
||||
`;
|
||||
|
||||
writeFileSync(gitCommitFile, fileContent);
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GENERATED_DIR="packages/cli/src/generated"
|
||||
GIT_COMMIT_FILE="$GENERATED_DIR/git-commit.ts"
|
||||
GIT_COMMIT_INFO="N/A"
|
||||
|
||||
mkdir -p "$GENERATED_DIR"
|
||||
|
||||
if command -v git &> /dev/null && git rev-parse --is-inside-work-tree &> /dev/null; then
|
||||
GIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
if [ -n "$GIT_HASH" ]; then
|
||||
GIT_COMMIT_INFO="$GIT_HASH"
|
||||
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
||||
GIT_COMMIT_INFO="$GIT_HASH (local modifications)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
cat <<EOL > "$GIT_COMMIT_FILE"
|
||||
/**
|
||||
* @license
|
||||
* Copyright $(date +%Y) Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// This file is auto-generated by the build script (scripts/build.sh)
|
||||
// Do not edit this file manually.
|
||||
export const GIT_COMMIT_INFO = '$GIT_COMMIT_INFO';
|
||||
EOL
|
||||
55
scripts/publish-sandbox.js
Normal file
55
scripts/publish-sandbox.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const {
|
||||
SANDBOX_IMAGE_REGISTRY,
|
||||
SANDBOX_IMAGE_NAME,
|
||||
npm_package_version,
|
||||
DOCKER_DRY_RUN,
|
||||
} = process.env;
|
||||
|
||||
if (!SANDBOX_IMAGE_REGISTRY) {
|
||||
console.error(
|
||||
'Error: SANDBOX_IMAGE_REGISTRY environment variable is not set.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!SANDBOX_IMAGE_NAME) {
|
||||
console.error('Error: SANDBOX_IMAGE_NAME environment variable is not set.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!npm_package_version) {
|
||||
console.error(
|
||||
'Error: npm_package_version environment variable is not set (should be run via npm).',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const imageUri = `${SANDBOX_IMAGE_REGISTRY}/${SANDBOX_IMAGE_NAME}:${npm_package_version}`;
|
||||
|
||||
if (DOCKER_DRY_RUN) {
|
||||
console.log(`DRY RUN: Would execute: docker push "${imageUri}"`);
|
||||
} else {
|
||||
console.log(`Executing: docker push "${imageUri}"`);
|
||||
execSync(`docker push "${imageUri}"`, { stdio: 'inherit' });
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Ensure required environment variables are set
|
||||
if [ -z "${SANDBOX_IMAGE_REGISTRY}" ]; then
|
||||
echo "Error: SANDBOX_IMAGE_REGISTRY environment variable is not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${SANDBOX_IMAGE_NAME}" ]; then
|
||||
echo "Error: SANDBOX_IMAGE_NAME environment variable is not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${npm_package_version}" ]; then
|
||||
echo "Error: npm_package_version environment variable is not set (should be run via npm)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IMAGE_URI="${SANDBOX_IMAGE_REGISTRY}/${SANDBOX_IMAGE_NAME}:${npm_package_version}"
|
||||
|
||||
if [ -n "${DOCKER_DRY_RUN:-}" ]; then
|
||||
echo "DRY RUN: Would execute: docker push \"${IMAGE_URI}\""
|
||||
else
|
||||
echo "Executing: docker push \"${IMAGE_URI}\""
|
||||
docker push "${IMAGE_URI}"
|
||||
fi
|
||||
123
scripts/sandbox.js
Normal file
123
scripts/sandbox.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
try {
|
||||
execSync('node scripts/sandbox_command.js -q');
|
||||
} catch {
|
||||
console.error('ERROR: sandboxing disabled. See docs to enable sandboxing.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = yargs(hideBin(process.argv)).option('i', {
|
||||
alias: 'interactive',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
}).argv;
|
||||
|
||||
if (argv.i && !process.stdin.isTTY) {
|
||||
console.error(
|
||||
'ERROR: interactive mode (-i) requested without a terminal attached',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const image = 'gemini-cli-sandbox';
|
||||
const sandboxCommand = execSync('node scripts/sandbox_command.js')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
const sandboxes = execSync(
|
||||
`${sandboxCommand} ps --filter "ancestor=${image}" --format "{{.Names}}"`,
|
||||
)
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
|
||||
let sandboxName;
|
||||
const firstArg = argv._[0];
|
||||
|
||||
if (firstArg) {
|
||||
if (firstArg.startsWith(image) || /^\d+$/.test(firstArg)) {
|
||||
sandboxName = firstArg.startsWith(image)
|
||||
? firstArg
|
||||
: `${image}-${firstArg}`;
|
||||
argv._.shift();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sandboxName) {
|
||||
if (sandboxes.length === 0) {
|
||||
console.error(
|
||||
'No sandboxes found. Are you running gemini-cli with sandboxing enabled?',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (sandboxes.length > 1) {
|
||||
console.error('Multiple sandboxes found:');
|
||||
sandboxes.forEach((s) => console.error(` ${s}`));
|
||||
console.error(
|
||||
'Sandbox name or index (0,1,...) must be specified as first argument',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
sandboxName = sandboxes[0];
|
||||
}
|
||||
|
||||
if (!sandboxes.includes(sandboxName)) {
|
||||
console.error(`unknown sandbox ${sandboxName}`);
|
||||
console.error('known sandboxes:');
|
||||
sandboxes.forEach((s) => console.error(` ${s}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const execArgs = [];
|
||||
let commandToRun = [];
|
||||
|
||||
// Determine interactive flags.
|
||||
// If a command is provided, only be interactive if -i is passed.
|
||||
// If no command is provided, always be interactive.
|
||||
if (argv._.length > 0) {
|
||||
if (argv.i) {
|
||||
execArgs.push('-it');
|
||||
}
|
||||
} else {
|
||||
execArgs.push('-it');
|
||||
}
|
||||
|
||||
// Determine the command to run inside the container.
|
||||
if (argv._.length > 0) {
|
||||
// Join all positional arguments into a single command string.
|
||||
const userCommand = argv._.join(' ');
|
||||
// The container is Linux, so we use bash -l -c to execute the command string.
|
||||
// This is cross-platform because it's what the container runs, not the host.
|
||||
commandToRun = ['bash', '-l', '-c', userCommand];
|
||||
} else {
|
||||
// No command provided, so we start an interactive bash login shell.
|
||||
commandToRun = ['bash', '-l'];
|
||||
}
|
||||
|
||||
const spawnArgs = ['exec', ...execArgs, sandboxName, ...commandToRun];
|
||||
|
||||
// Use spawn to avoid shell injection issues and handle arguments correctly.
|
||||
spawn(sandboxCommand, spawnArgs, { stdio: 'inherit' });
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if ! scripts/sandbox_command.sh -q; then
|
||||
echo "ERROR: sandboxing disabled. See docs to enable sandboxing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# parse flags
|
||||
interactive=false
|
||||
while getopts "i" opt; do
|
||||
case "$opt" in
|
||||
\?)
|
||||
echo "usage: sandbox.sh [-i] [sandbox-name-or-index = AUTO] [command... = bash -l]"
|
||||
echo " -i: enable interactive mode for custom command (enabled by default for login shell)"
|
||||
echo " (WARNING: interactive mode causes stderr to be redirected to stdout)"
|
||||
exit 1
|
||||
;;
|
||||
i)
|
||||
interactive=true
|
||||
if [ ! -t 0 ]; then
|
||||
echo "ERROR: interactive mode (-i) requested without a terminal attached"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
IMAGE=gemini-cli-sandbox
|
||||
CMD=$(scripts/sandbox_command.sh)
|
||||
|
||||
# list all containers running on sandbox image
|
||||
sandboxes=()
|
||||
while IFS= read -r line; do
|
||||
sandboxes+=("$line")
|
||||
done < <($CMD ps --filter "ancestor=$IMAGE" --format "{{.Names}}")
|
||||
|
||||
# take first argument as sandbox name if it starts with image name or is an integer
|
||||
# otherwise require a unique sandbox to be running and take its name
|
||||
if [[ "${1:-}" =~ ^$IMAGE(-[0-9]+)?$ ]]; then
|
||||
SANDBOX=$1
|
||||
shift
|
||||
elif [[ "${1:-}" =~ ^[0-9]+$ ]]; then
|
||||
SANDBOX=$IMAGE-$1
|
||||
shift
|
||||
else
|
||||
# exit if no sandbox is running
|
||||
if [ ${#sandboxes[@]} -eq 0 ]; then
|
||||
echo "No sandboxes found. Are you running gemini-cli with sandboxing enabled?"
|
||||
exit 1
|
||||
fi
|
||||
# exit if multiple sandboxes are running
|
||||
if [ ${#sandboxes[@]} -gt 1 ]; then
|
||||
echo "Multiple sandboxes found:"
|
||||
for sandbox in "${sandboxes[@]}"; do
|
||||
echo " $sandbox"
|
||||
done
|
||||
echo "Sandbox name or index (0,1,...) must be specified as first argument"
|
||||
exit 1
|
||||
fi
|
||||
SANDBOX=${sandboxes[0]}
|
||||
fi
|
||||
|
||||
# check that sandbox exists
|
||||
if ! [[ " ${sandboxes[*]} " == *" $SANDBOX "* ]]; then
|
||||
echo "unknown sandbox $SANDBOX"
|
||||
echo "known sandboxes:"
|
||||
for sandbox in "${sandboxes[@]}"; do
|
||||
echo " $sandbox"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# determine command and args for exec
|
||||
if [ $# -gt 0 ]; then
|
||||
cmd=(bash -l -c "$(printf '%q ' "$@")") # fixes quoting, e.g. bash -c 'echo $SANDBOX'
|
||||
exec_args=()
|
||||
if [ "$interactive" = true ]; then
|
||||
exec_args=(-it)
|
||||
fi
|
||||
else
|
||||
cmd=(bash -l)
|
||||
exec_args=(-it)
|
||||
fi
|
||||
|
||||
# run command in sandbox
|
||||
exec_args+=("$SANDBOX" "${cmd[@]}")
|
||||
$CMD exec "${exec_args[@]}"
|
||||
126
scripts/sandbox_command.js
Normal file
126
scripts/sandbox_command.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import os from 'os';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
const argv = yargs(hideBin(process.argv)).option('q', {
|
||||
alias: 'quiet',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
}).argv;
|
||||
|
||||
let geminiSandbox = process.env.GEMINI_SANDBOX;
|
||||
|
||||
if (!geminiSandbox) {
|
||||
const userSettingsFile = join(os.homedir(), '.gemini', 'settings.json');
|
||||
if (existsSync(userSettingsFile)) {
|
||||
const settings = JSON.parse(readFileSync(userSettingsFile, 'utf-8'));
|
||||
if (settings.sandbox) {
|
||||
geminiSandbox = settings.sandbox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!geminiSandbox) {
|
||||
let currentDir = process.cwd();
|
||||
while (currentDir !== '/') {
|
||||
const geminiEnv = join(currentDir, '.gemini', '.env');
|
||||
const regularEnv = join(currentDir, '.env');
|
||||
if (existsSync(geminiEnv)) {
|
||||
dotenv.config({ path: geminiEnv });
|
||||
break;
|
||||
} else if (existsSync(regularEnv)) {
|
||||
dotenv.config({ path: regularEnv });
|
||||
break;
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
geminiSandbox = process.env.GEMINI_SANDBOX;
|
||||
}
|
||||
|
||||
if (process.env.GEMINI_CODE_SANDBOX) {
|
||||
console.warn(
|
||||
'WARNING: GEMINI_CODE_SANDBOX is deprecated. Use GEMINI_SANDBOX instead.',
|
||||
);
|
||||
geminiSandbox = process.env.GEMINI_CODE_SANDBOX;
|
||||
}
|
||||
|
||||
geminiSandbox = (geminiSandbox || '').toLowerCase();
|
||||
|
||||
const commandExists = (cmd) => {
|
||||
const checkCommand = os.platform() === 'win32' ? 'where' : 'command -v';
|
||||
try {
|
||||
execSync(`${checkCommand} ${cmd}`, { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch {
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
execSync(`${checkCommand} ${cmd}.exe`, { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let command = '';
|
||||
if (['1', 'true'].includes(geminiSandbox)) {
|
||||
if (commandExists('docker')) {
|
||||
command = 'docker';
|
||||
} else if (commandExists('podman')) {
|
||||
command = 'podman';
|
||||
} else {
|
||||
console.error(
|
||||
'ERROR: install docker or podman or specify command in GEMINI_SANDBOX',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (geminiSandbox && !['0', 'false'].includes(geminiSandbox)) {
|
||||
if (commandExists(geminiSandbox)) {
|
||||
command = geminiSandbox;
|
||||
} else {
|
||||
console.error(
|
||||
`ERROR: missing sandbox command '${geminiSandbox}' (from GEMINI_SANDBOX)`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
if (os.platform() === 'darwin' && process.env.SEATBELT_PROFILE !== 'none') {
|
||||
if (commandExists('sandbox-exec')) {
|
||||
command = 'sandbox-exec';
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!argv.q) {
|
||||
console.log(command);
|
||||
}
|
||||
process.exit(0);
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# usage: scripts/sandbox_command.sh [-q]
|
||||
# -q: quiet mode (do not print command, just exit w/ code 0 or 1)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# parse flags
|
||||
QUIET=false
|
||||
while getopts ":q" opt; do
|
||||
case ${opt} in
|
||||
q) QUIET=true ;;
|
||||
\?)
|
||||
echo "Usage: $0 [-q]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
# if GEMINI_SANDBOX is not set, see if it is set in user settings
|
||||
# note it can be string or boolean, and if missing `npx json` will return empty string
|
||||
USER_SETTINGS_FILE="$HOME/.gemini/settings.json"
|
||||
if [ -z "${GEMINI_SANDBOX:-}" ] && [ -f "$USER_SETTINGS_FILE" ]; then
|
||||
# Check if jq is available (more reliable than npx json)
|
||||
if command -v jq &>/dev/null; then
|
||||
USER_SANDBOX_SETTING=$(jq -r '.sandbox // empty' "$USER_SETTINGS_FILE" 2>/dev/null || echo "")
|
||||
else
|
||||
# Fallback to npx json with error handling
|
||||
USER_SANDBOX_SETTING=$(sed -e 's/\/\/.*//' -e 's/\/\*.*\*\///g' -e '/^[[:space:]]*\/\//d' "$USER_SETTINGS_FILE" | npx json 'sandbox' 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Avoid setting GEMINI_SANDBOX to complex objects
|
||||
if [ -n "$USER_SANDBOX_SETTING" ] && [[ ! "$USER_SANDBOX_SETTING" =~ ^\{.*\}$ ]]; then
|
||||
GEMINI_SANDBOX=$USER_SANDBOX_SETTING
|
||||
fi
|
||||
fi
|
||||
|
||||
# if GEMINI_SANDBOX is not set, try to source .env in case set there
|
||||
# allow .env to be in any ancestor directory (same as findEnvFile in config.ts)
|
||||
# prefer gemini-specific .env under .gemini folder (also same as in findEnvFile)
|
||||
if [ -z "${GEMINI_SANDBOX:-}" ]; then
|
||||
current_dir=$(pwd)
|
||||
dot_env_sourced=false
|
||||
while [ "$current_dir" != "/" ]; do
|
||||
if [ -f "$current_dir/.gemini/.env" ]; then
|
||||
source "$current_dir/.gemini/.env"
|
||||
dot_env_sourced=true
|
||||
break
|
||||
elif [ -f "$current_dir/.env" ]; then
|
||||
source "$current_dir/.env"
|
||||
dot_env_sourced=true
|
||||
break
|
||||
fi
|
||||
current_dir=$(dirname "$current_dir")
|
||||
done
|
||||
# if .env is not found in any ancestor directory, try home as fallback
|
||||
if [ "$dot_env_sourced" = false ]; then
|
||||
if [ -f "$HOME/.gemini/.env" ]; then
|
||||
source "$HOME/.gemini/.env"
|
||||
dot_env_sourced=true
|
||||
elif [ -f "$HOME/.env" ]; then
|
||||
source "$HOME/.env"
|
||||
dot_env_sourced=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# copy and warn about deprecated GEMINI_CODE_SANDBOX
|
||||
if [ -n "${GEMINI_CODE_SANDBOX:-}" ]; then
|
||||
echo "WARNING: GEMINI_CODE_SANDBOX is deprecated. Use GEMINI_SANDBOX instead." >&2
|
||||
GEMINI_SANDBOX=$GEMINI_CODE_SANDBOX
|
||||
export GEMINI_SANDBOX
|
||||
fi
|
||||
|
||||
# lowercase GEMINI_SANDBOX
|
||||
GEMINI_SANDBOX=$(echo "${GEMINI_SANDBOX:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# if GEMINI_SANDBOX is set to 1|true, then try to use docker or podman
|
||||
# if non-empty and not 0|false, treat as custom command and check that it exists
|
||||
# if empty or 0|false, then fail silently (after checking for possible fallbacks)
|
||||
command=""
|
||||
if [[ "${GEMINI_SANDBOX:-}" =~ ^(1|true)$ ]]; then
|
||||
if command -v docker &>/dev/null; then
|
||||
command="docker"
|
||||
elif command -v podman &>/dev/null; then
|
||||
command="podman"
|
||||
else
|
||||
echo "ERROR: install docker or podman or specify command in GEMINI_SANDBOX" >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [ -n "${GEMINI_SANDBOX:-}" ] && [[ ! "${GEMINI_SANDBOX:-}" =~ ^(0|false)$ ]]; then
|
||||
if ! command -v "$GEMINI_SANDBOX" &>/dev/null; then
|
||||
echo "ERROR: missing sandbox command '$GEMINI_SANDBOX' (from GEMINI_SANDBOX)" >&2
|
||||
exit 1
|
||||
fi
|
||||
command="$GEMINI_SANDBOX"
|
||||
else
|
||||
# if we are on macOS and sandbox-exec is available, use that for minimal sandboxing
|
||||
# unless SEATBELT_PROFILE is set to 'none', which we allow as an escape hatch
|
||||
if [ "$(uname)" = "Darwin" ] && command -v sandbox-exec &>/dev/null && [ "${SEATBELT_PROFILE:-}" != "none" ]; then
|
||||
command="sandbox-exec"
|
||||
else # GEMINI_SANDBOX is empty or 0|false, so we fail w/o error msg
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$QUIET" = false ]; then echo "$command"; fi
|
||||
exit 0
|
||||
42
scripts/setup-dev.js
Normal file
42
scripts/setup-dev.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
try {
|
||||
execSync('command -v npm', { stdio: 'ignore' });
|
||||
} catch {
|
||||
console.log('npm not found. Installing npm via nvm...');
|
||||
try {
|
||||
execSync(
|
||||
'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash',
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
const nvmsh = `\\. "$HOME/.nvm/nvm.sh"`;
|
||||
execSync(`${nvmsh} && nvm install 22`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && node -v`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && nvm current`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && npm -v`, { stdio: 'inherit' });
|
||||
} catch {
|
||||
console.error('Failed to install nvm or node.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Development environment setup complete.');
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Check if npm is installed
|
||||
if ! command -v npm &>/dev/null; then
|
||||
echo "npm not found. Installing npm via nvm..."
|
||||
# Download and install nvm:
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
||||
# in lieu of restarting the shell
|
||||
\. "$HOME/.nvm/nvm.sh"
|
||||
# Download and install Node.js:
|
||||
nvm install 22
|
||||
# Verify the Node.js version:
|
||||
node -v # Should print "v22.15.0".
|
||||
nvm current # Should print "v22.15.0".
|
||||
# Verify npm version:
|
||||
npm -v # Should print "10.9.2".
|
||||
fi
|
||||
|
||||
echo "Development environment setup complete."
|
||||
61
scripts/start.js
Normal file
61
scripts/start.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law_or_agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { spawn, execSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
const root = join(import.meta.dirname, '..');
|
||||
|
||||
// check build status, write warnings to file for app to display if needed
|
||||
execSync('node ./scripts/check-build-status.js', {
|
||||
stdio: 'inherit',
|
||||
cwd: root,
|
||||
});
|
||||
|
||||
// if debugging is enabled and sandboxing is disabled, use --inspect-brk flag
|
||||
// note with sandboxing this flag is passed to the binary inside the sandbox
|
||||
// inside sandbox SANDBOX should be set and sandbox_command.js should fail
|
||||
const nodeArgs = [];
|
||||
try {
|
||||
execSync('node scripts/sandbox_command.js -q', {
|
||||
stdio: 'inherit',
|
||||
cwd: root,
|
||||
});
|
||||
if (process.env.DEBUG) {
|
||||
if (process.env.SANDBOX) {
|
||||
const port = process.env.DEBUG_PORT || '9229';
|
||||
nodeArgs.push(`--inspect-brk=0.0.0.0:${port}`);
|
||||
} else {
|
||||
nodeArgs.push('--inspect-brk');
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
nodeArgs.push('./packages/cli');
|
||||
nodeArgs.push(...process.argv.slice(2));
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
CLI_VERSION: 'development',
|
||||
DEV: 'true',
|
||||
};
|
||||
|
||||
spawn('node', nodeArgs, { stdio: 'inherit', env });
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# check build status, write warnings to file for app to display if needed
|
||||
node ./scripts/check-build-status.js
|
||||
|
||||
# if debugging is enabled and sandboxing is disabled, use --inspect-brk flag
|
||||
# note with sandboxing this flag is passed to the binary inside the sandbox
|
||||
# inside sandbox SANDBOX should be set and sandbox_command.sh should fail
|
||||
node_args=()
|
||||
if [ -n "${DEBUG:-}" ] && ! scripts/sandbox_command.sh -q; then
|
||||
if [ -n "${SANDBOX:-}" ]; then
|
||||
port="${DEBUG_PORT:-9229}"
|
||||
node_args=("--inspect-brk=0.0.0.0:$port")
|
||||
else
|
||||
node_args=(--inspect-brk)
|
||||
fi
|
||||
fi
|
||||
node_args+=("./packages/cli" "$@")
|
||||
|
||||
# DEV=true to enable React Dev Tools (https://github.com/vadimdemedes/ink?tab=readme-ov-file#using-react-devtools)
|
||||
# CLI_VERSION to display in the app ui footer
|
||||
CLI_VERSION='development' DEV=true node "${node_args[@]}"
|
||||
Reference in New Issue
Block a user