@mmmbuto/qwen-code-termux 0.6.4-termux → 0.6.401
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/README.md +3 -3
- package/dist/cli.js +4 -4
- package/package.json +3 -2
- package/scripts/build.js +55 -0
- package/scripts/build_package.js +37 -0
- package/scripts/build_sandbox.js +196 -0
- package/scripts/build_vscode_companion.js +30 -0
- package/scripts/check-build-status.js +148 -0
- package/scripts/check-i18n.ts +462 -0
- package/scripts/check-lockfile.js +74 -0
- package/scripts/clean.js +59 -0
- package/scripts/copy_bundle_assets.js +128 -0
- package/scripts/copy_files.js +86 -0
- package/scripts/create_alias.sh +39 -0
- package/scripts/esbuild-shims.js +29 -0
- package/scripts/generate-git-commit-info.js +71 -0
- package/scripts/get-release-version.js +411 -0
- package/scripts/lint.js +205 -0
- package/scripts/local_telemetry.js +219 -0
- package/scripts/postinstall.cjs +13 -0
- package/scripts/pre-commit.js +22 -0
- package/scripts/prepare-package.js +162 -0
- package/scripts/sandbox_command.js +126 -0
- package/scripts/start.js +83 -0
- package/scripts/telemetry.js +85 -0
- package/scripts/telemetry_gcp.js +188 -0
- package/scripts/telemetry_utils.js +439 -0
- package/scripts/termux-tools/call.sh +206 -0
- package/scripts/termux-tools/discovery.sh +382 -0
- package/scripts/test-windows-paths.js +51 -0
- package/scripts/tests/get-release-version.test.js +186 -0
- package/scripts/tests/test-setup.ts +12 -0
- package/scripts/tests/vitest.config.ts +26 -0
- package/scripts/unused-keys-only-in-locales.json +61 -0
- package/scripts/version.js +112 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2025 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import { spawn, execSync } from 'node:child_process';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import {
|
|
14
|
+
BIN_DIR,
|
|
15
|
+
OTEL_DIR,
|
|
16
|
+
ensureBinary,
|
|
17
|
+
fileExists,
|
|
18
|
+
manageTelemetrySettings,
|
|
19
|
+
registerCleanup,
|
|
20
|
+
waitForPort,
|
|
21
|
+
} from './telemetry_utils.js';
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
const OTEL_CONFIG_FILE = path.join(OTEL_DIR, 'collector-local.yaml');
|
|
27
|
+
const OTEL_LOG_FILE = path.join(OTEL_DIR, 'collector.log');
|
|
28
|
+
const JAEGER_LOG_FILE = path.join(OTEL_DIR, 'jaeger.log');
|
|
29
|
+
const JAEGER_PORT = 16686;
|
|
30
|
+
|
|
31
|
+
// This configuration is for the primary otelcol-contrib instance.
|
|
32
|
+
// It receives from the CLI on 4317, exports traces to Jaeger on 14317,
|
|
33
|
+
// and sends metrics/logs to the debug log.
|
|
34
|
+
const OTEL_CONFIG_CONTENT = `
|
|
35
|
+
receivers:
|
|
36
|
+
otlp:
|
|
37
|
+
protocols:
|
|
38
|
+
grpc:
|
|
39
|
+
endpoint: "localhost:4317"
|
|
40
|
+
processors:
|
|
41
|
+
batch:
|
|
42
|
+
timeout: 1s
|
|
43
|
+
exporters:
|
|
44
|
+
otlp:
|
|
45
|
+
endpoint: "localhost:14317"
|
|
46
|
+
tls:
|
|
47
|
+
insecure: true
|
|
48
|
+
debug:
|
|
49
|
+
verbosity: detailed
|
|
50
|
+
service:
|
|
51
|
+
telemetry:
|
|
52
|
+
logs:
|
|
53
|
+
level: "debug"
|
|
54
|
+
metrics:
|
|
55
|
+
level: "none"
|
|
56
|
+
pipelines:
|
|
57
|
+
traces:
|
|
58
|
+
receivers: [otlp]
|
|
59
|
+
processors: [batch]
|
|
60
|
+
exporters: [otlp]
|
|
61
|
+
metrics:
|
|
62
|
+
receivers: [otlp]
|
|
63
|
+
processors: [batch]
|
|
64
|
+
exporters: [debug]
|
|
65
|
+
logs:
|
|
66
|
+
receivers: [otlp]
|
|
67
|
+
processors: [batch]
|
|
68
|
+
exporters: [debug]
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
// 1. Ensure binaries are available, downloading if necessary.
|
|
73
|
+
// Binaries are stored in the project's .qwen/otel/bin directory
|
|
74
|
+
// to avoid modifying the user's system.
|
|
75
|
+
if (!fileExists(BIN_DIR)) fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
76
|
+
|
|
77
|
+
const otelcolPath = await ensureBinary(
|
|
78
|
+
'otelcol-contrib',
|
|
79
|
+
'open-telemetry/opentelemetry-collector-releases',
|
|
80
|
+
(version, platform, arch, ext) =>
|
|
81
|
+
`otelcol-contrib_${version}_${platform}_${arch}.${ext}`,
|
|
82
|
+
'otelcol-contrib',
|
|
83
|
+
false, // isJaeger = false
|
|
84
|
+
).catch((e) => {
|
|
85
|
+
console.error(`🛑 Error getting otelcol-contrib: ${e.message}`);
|
|
86
|
+
return null;
|
|
87
|
+
});
|
|
88
|
+
if (!otelcolPath) process.exit(1);
|
|
89
|
+
|
|
90
|
+
const jaegerPath = await ensureBinary(
|
|
91
|
+
'jaeger',
|
|
92
|
+
'jaegertracing/jaeger',
|
|
93
|
+
(version, platform, arch, ext) =>
|
|
94
|
+
`jaeger-${version}-${platform}-${arch}.${ext}`,
|
|
95
|
+
'jaeger',
|
|
96
|
+
true, // isJaeger = true
|
|
97
|
+
).catch((e) => {
|
|
98
|
+
console.error(`🛑 Error getting jaeger: ${e.message}`);
|
|
99
|
+
return null;
|
|
100
|
+
});
|
|
101
|
+
if (!jaegerPath) process.exit(1);
|
|
102
|
+
|
|
103
|
+
// 2. Kill any existing processes to ensure a clean start.
|
|
104
|
+
console.log('🧹 Cleaning up old processes and logs...');
|
|
105
|
+
try {
|
|
106
|
+
execSync('pkill -f "otelcol-contrib"');
|
|
107
|
+
console.log('✅ Stopped existing otelcol-contrib process.');
|
|
108
|
+
} catch (_e) {} // eslint-disable-line no-empty
|
|
109
|
+
try {
|
|
110
|
+
execSync('pkill -f "jaeger"');
|
|
111
|
+
console.log('✅ Stopped existing jaeger process.');
|
|
112
|
+
} catch (_e) {} // eslint-disable-line no-empty
|
|
113
|
+
try {
|
|
114
|
+
if (fileExists(OTEL_LOG_FILE)) fs.unlinkSync(OTEL_LOG_FILE);
|
|
115
|
+
console.log('✅ Deleted old collector log.');
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (e.code !== 'ENOENT') console.error(e);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
if (fileExists(JAEGER_LOG_FILE)) fs.unlinkSync(JAEGER_LOG_FILE);
|
|
121
|
+
console.log('✅ Deleted old jaeger log.');
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (e.code !== 'ENOENT') console.error(e);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let jaegerProcess, collectorProcess;
|
|
127
|
+
let jaegerLogFd, collectorLogFd;
|
|
128
|
+
|
|
129
|
+
const originalSandboxSetting = manageTelemetrySettings(
|
|
130
|
+
true,
|
|
131
|
+
'http://localhost:4317',
|
|
132
|
+
'local',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
registerCleanup(
|
|
136
|
+
() => [jaegerProcess, collectorProcess],
|
|
137
|
+
() => [jaegerLogFd, collectorLogFd],
|
|
138
|
+
originalSandboxSetting,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!fileExists(OTEL_DIR)) fs.mkdirSync(OTEL_DIR, { recursive: true });
|
|
142
|
+
fs.writeFileSync(OTEL_CONFIG_FILE, OTEL_CONFIG_CONTENT);
|
|
143
|
+
console.log('📄 Wrote OTEL collector config.');
|
|
144
|
+
|
|
145
|
+
// Start Jaeger
|
|
146
|
+
console.log(`🚀 Starting Jaeger service... Logs: ${JAEGER_LOG_FILE}`);
|
|
147
|
+
jaegerLogFd = fs.openSync(JAEGER_LOG_FILE, 'a');
|
|
148
|
+
jaegerProcess = spawn(
|
|
149
|
+
jaegerPath,
|
|
150
|
+
['--set=receivers.otlp.protocols.grpc.endpoint=localhost:14317'],
|
|
151
|
+
{ stdio: ['ignore', jaegerLogFd, jaegerLogFd] },
|
|
152
|
+
);
|
|
153
|
+
console.log(`⏳ Waiting for Jaeger to start (PID: ${jaegerProcess.pid})...`);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await waitForPort(JAEGER_PORT);
|
|
157
|
+
console.log(`✅ Jaeger started successfully.`);
|
|
158
|
+
} catch (_) {
|
|
159
|
+
console.error(`🛑 Error: Jaeger failed to start on port ${JAEGER_PORT}.`);
|
|
160
|
+
if (jaegerProcess && jaegerProcess.pid) {
|
|
161
|
+
process.kill(jaegerProcess.pid, 'SIGKILL');
|
|
162
|
+
}
|
|
163
|
+
if (fileExists(JAEGER_LOG_FILE)) {
|
|
164
|
+
console.error('📄 Jaeger Log Output:');
|
|
165
|
+
console.error(fs.readFileSync(JAEGER_LOG_FILE, 'utf-8'));
|
|
166
|
+
}
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Start the primary OTEL collector
|
|
171
|
+
console.log(`🚀 Starting OTEL collector... Logs: ${OTEL_LOG_FILE}`);
|
|
172
|
+
collectorLogFd = fs.openSync(OTEL_LOG_FILE, 'a');
|
|
173
|
+
collectorProcess = spawn(otelcolPath, ['--config', OTEL_CONFIG_FILE], {
|
|
174
|
+
stdio: ['ignore', collectorLogFd, collectorLogFd],
|
|
175
|
+
});
|
|
176
|
+
console.log(
|
|
177
|
+
`⏳ Waiting for OTEL collector to start (PID: ${collectorProcess.pid})...`,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await waitForPort(4317);
|
|
182
|
+
console.log(`✅ OTEL collector started successfully.`);
|
|
183
|
+
} catch (_) {
|
|
184
|
+
console.error(`🛑 Error: OTEL collector failed to start on port 4317.`);
|
|
185
|
+
if (collectorProcess && collectorProcess.pid) {
|
|
186
|
+
process.kill(collectorProcess.pid, 'SIGKILL');
|
|
187
|
+
}
|
|
188
|
+
if (fileExists(OTEL_LOG_FILE)) {
|
|
189
|
+
console.error('📄 OTEL Collector Log Output:');
|
|
190
|
+
console.error(fs.readFileSync(OTEL_LOG_FILE, 'utf-8'));
|
|
191
|
+
}
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
[jaegerProcess, collectorProcess].forEach((proc) => {
|
|
196
|
+
if (proc) {
|
|
197
|
+
proc.on('error', (err) => {
|
|
198
|
+
console.error(`${proc.spawnargs[0]} process error:`, err);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
console.log(`
|
|
205
|
+
✨ Local telemetry environment is running.`);
|
|
206
|
+
console.log(
|
|
207
|
+
`
|
|
208
|
+
🔎 View traces in the Jaeger UI: http://localhost:${JAEGER_PORT}`,
|
|
209
|
+
);
|
|
210
|
+
console.log(`📊 View metrics in the logs and metrics: ${OTEL_LOG_FILE}`);
|
|
211
|
+
console.log(
|
|
212
|
+
`
|
|
213
|
+
📄 Tail logs and metrics in another terminal: tail -f ${OTEL_LOG_FILE}`,
|
|
214
|
+
);
|
|
215
|
+
console.log(`
|
|
216
|
+
Press Ctrl+C to exit.`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
main();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// TERMUX: Minimal install message
|
|
8
|
+
const os = require('node:os');
|
|
9
|
+
|
|
10
|
+
if (os.platform() === 'android' || process.env.PREFIX?.includes('com.termux')) {
|
|
11
|
+
console.log('✓ qwen-code-termux installed');
|
|
12
|
+
console.log(' Run: qwen');
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import lintStaged from 'lint-staged';
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
// Get repository root
|
|
12
|
+
const root = execSync('git rev-parse --show-toplevel').toString().trim();
|
|
13
|
+
|
|
14
|
+
// Run lint-staged with API directly
|
|
15
|
+
const passed = await lintStaged({ cwd: root });
|
|
16
|
+
|
|
17
|
+
// Exit with appropriate code
|
|
18
|
+
process.exit(passed ? 0 : 1);
|
|
19
|
+
} catch {
|
|
20
|
+
// Exit with error code
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Qwen
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Prepares the bundled CLI package for npm publishing
|
|
9
|
+
* This script adds publishing metadata (package.json, README, LICENSE) to dist/
|
|
10
|
+
* All runtime assets (cli.js, vendor/, *.sb) are already in dist/ from the bundle step
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
21
|
+
|
|
22
|
+
const distDir = path.join(rootDir, 'dist');
|
|
23
|
+
const cliBundlePath = path.join(distDir, 'cli.js');
|
|
24
|
+
const vendorDir = path.join(distDir, 'vendor');
|
|
25
|
+
|
|
26
|
+
// Verify dist directory and bundle exist
|
|
27
|
+
if (!fs.existsSync(distDir)) {
|
|
28
|
+
console.error('Error: dist/ directory not found');
|
|
29
|
+
console.error('Please run "npm run bundle" first');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(cliBundlePath)) {
|
|
34
|
+
console.error(`Error: Bundle not found at ${cliBundlePath}`);
|
|
35
|
+
console.error('Please run "npm run bundle" first');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!fs.existsSync(vendorDir)) {
|
|
40
|
+
console.error(`Error: Vendor directory not found at ${vendorDir}`);
|
|
41
|
+
console.error('Please run "npm run bundle" first');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Copy README and LICENSE
|
|
46
|
+
console.log('Copying documentation files...');
|
|
47
|
+
const filesToCopy = ['README.md', 'LICENSE'];
|
|
48
|
+
for (const file of filesToCopy) {
|
|
49
|
+
const sourcePath = path.join(rootDir, file);
|
|
50
|
+
const destPath = path.join(distDir, file);
|
|
51
|
+
if (fs.existsSync(sourcePath)) {
|
|
52
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
53
|
+
console.log(`Copied ${file}`);
|
|
54
|
+
} else {
|
|
55
|
+
console.warn(`Warning: ${file} not found at ${sourcePath}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Copy locales folder
|
|
60
|
+
console.log('Copying locales folder...');
|
|
61
|
+
const localesSourceDir = path.join(
|
|
62
|
+
rootDir,
|
|
63
|
+
'packages',
|
|
64
|
+
'cli',
|
|
65
|
+
'src',
|
|
66
|
+
'i18n',
|
|
67
|
+
'locales',
|
|
68
|
+
);
|
|
69
|
+
const localesDestDir = path.join(distDir, 'locales');
|
|
70
|
+
|
|
71
|
+
if (fs.existsSync(localesSourceDir)) {
|
|
72
|
+
// Recursive copy function
|
|
73
|
+
function copyRecursiveSync(src, dest) {
|
|
74
|
+
const stats = fs.statSync(src);
|
|
75
|
+
if (stats.isDirectory()) {
|
|
76
|
+
if (!fs.existsSync(dest)) {
|
|
77
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
const entries = fs.readdirSync(src);
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const srcPath = path.join(src, entry);
|
|
82
|
+
const destPath = path.join(dest, entry);
|
|
83
|
+
copyRecursiveSync(srcPath, destPath);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
fs.copyFileSync(src, dest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
copyRecursiveSync(localesSourceDir, localesDestDir);
|
|
91
|
+
console.log('Copied locales folder');
|
|
92
|
+
} else {
|
|
93
|
+
console.warn(`Warning: locales folder not found at ${localesSourceDir}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Copy package.json from root and modify it for publishing
|
|
97
|
+
console.log('Creating package.json for distribution...');
|
|
98
|
+
const rootPackageJson = JSON.parse(
|
|
99
|
+
fs.readFileSync(path.join(rootDir, 'package.json'), 'utf-8'),
|
|
100
|
+
);
|
|
101
|
+
const corePackageJson = JSON.parse(
|
|
102
|
+
fs.readFileSync(
|
|
103
|
+
path.join(rootDir, 'packages', 'core', 'package.json'),
|
|
104
|
+
'utf-8',
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const runtimeDependencies = {};
|
|
109
|
+
if (corePackageJson.dependencies?.tiktoken) {
|
|
110
|
+
runtimeDependencies.tiktoken = corePackageJson.dependencies.tiktoken;
|
|
111
|
+
}
|
|
112
|
+
const isTermuxBuild =
|
|
113
|
+
process.platform === 'android' ||
|
|
114
|
+
!!process.env['TERMUX_VERSION'] ||
|
|
115
|
+
!!(process.env['PREFIX'] && process.env['PREFIX'].includes('com.termux'));
|
|
116
|
+
const optionalDependencies = isTermuxBuild
|
|
117
|
+
? undefined
|
|
118
|
+
: {
|
|
119
|
+
'@lydell/node-pty': '1.1.0',
|
|
120
|
+
'@lydell/node-pty-darwin-arm64': '1.1.0',
|
|
121
|
+
'@lydell/node-pty-darwin-x64': '1.1.0',
|
|
122
|
+
'@lydell/node-pty-linux-x64': '1.1.0',
|
|
123
|
+
'@lydell/node-pty-win32-arm64': '1.1.0',
|
|
124
|
+
'@lydell/node-pty-win32-x64': '1.1.0',
|
|
125
|
+
'node-pty': '^1.0.0',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Create a clean package.json for the published package
|
|
129
|
+
const distPackageJson = {
|
|
130
|
+
name: rootPackageJson.name,
|
|
131
|
+
version: rootPackageJson.version,
|
|
132
|
+
description:
|
|
133
|
+
rootPackageJson.description || 'Qwen Code - AI-powered coding assistant',
|
|
134
|
+
repository: rootPackageJson.repository,
|
|
135
|
+
type: 'module',
|
|
136
|
+
main: 'cli.js',
|
|
137
|
+
bin: {
|
|
138
|
+
qwen: 'cli.js',
|
|
139
|
+
},
|
|
140
|
+
files: [
|
|
141
|
+
'cli.js',
|
|
142
|
+
'vendor',
|
|
143
|
+
'*.sb',
|
|
144
|
+
'tiktoken_bg.wasm',
|
|
145
|
+
'README.md',
|
|
146
|
+
'LICENSE',
|
|
147
|
+
'locales',
|
|
148
|
+
],
|
|
149
|
+
config: rootPackageJson.config,
|
|
150
|
+
dependencies: runtimeDependencies,
|
|
151
|
+
engines: rootPackageJson.engines,
|
|
152
|
+
...(optionalDependencies ? { optionalDependencies } : {}),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
fs.writeFileSync(
|
|
156
|
+
path.join(distDir, 'package.json'),
|
|
157
|
+
JSON.stringify(distPackageJson, null, 2) + '\n',
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
console.log('\n✅ Package prepared for publishing at dist/');
|
|
161
|
+
console.log('\nPackage structure:');
|
|
162
|
+
execSync('ls -lh dist/', { stdio: 'inherit', cwd: rootDir });
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
// you may not use this file except in compliance with the License.
|
|
10
|
+
// You may obtain a copy of the License at
|
|
11
|
+
//
|
|
12
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
//
|
|
14
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
// See the License for the specific language governing permissions and
|
|
18
|
+
// limitations under the License.
|
|
19
|
+
|
|
20
|
+
import { execSync } from 'node:child_process';
|
|
21
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
22
|
+
import { join, dirname } from 'node:path';
|
|
23
|
+
import stripJsonComments from 'strip-json-comments';
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import yargs from 'yargs';
|
|
26
|
+
import { hideBin } from 'yargs/helpers';
|
|
27
|
+
import dotenv from 'dotenv';
|
|
28
|
+
|
|
29
|
+
const argv = yargs(hideBin(process.argv)).option('q', {
|
|
30
|
+
alias: 'quiet',
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
default: false,
|
|
33
|
+
}).argv;
|
|
34
|
+
|
|
35
|
+
let geminiSandbox = process.env.GEMINI_SANDBOX;
|
|
36
|
+
|
|
37
|
+
if (!geminiSandbox) {
|
|
38
|
+
const userSettingsFile = join(os.homedir(), '.qwen', 'settings.json');
|
|
39
|
+
if (existsSync(userSettingsFile)) {
|
|
40
|
+
const settings = JSON.parse(
|
|
41
|
+
stripJsonComments(readFileSync(userSettingsFile, 'utf-8')),
|
|
42
|
+
);
|
|
43
|
+
if (settings.sandbox) {
|
|
44
|
+
geminiSandbox = settings.sandbox;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!geminiSandbox) {
|
|
50
|
+
let currentDir = process.cwd();
|
|
51
|
+
while (true) {
|
|
52
|
+
const geminiEnv = join(currentDir, '.qwen', '.env');
|
|
53
|
+
const regularEnv = join(currentDir, '.env');
|
|
54
|
+
if (existsSync(geminiEnv)) {
|
|
55
|
+
dotenv.config({ path: geminiEnv, quiet: true });
|
|
56
|
+
break;
|
|
57
|
+
} else if (existsSync(regularEnv)) {
|
|
58
|
+
dotenv.config({ path: regularEnv, quiet: true });
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
const parentDir = dirname(currentDir);
|
|
62
|
+
if (parentDir === currentDir) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
currentDir = parentDir;
|
|
66
|
+
}
|
|
67
|
+
geminiSandbox = process.env.GEMINI_SANDBOX;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
geminiSandbox = (geminiSandbox || '').toLowerCase();
|
|
71
|
+
|
|
72
|
+
const commandExists = (cmd) => {
|
|
73
|
+
const checkCommand = os.platform() === 'win32' ? 'where' : 'command -v';
|
|
74
|
+
try {
|
|
75
|
+
execSync(`${checkCommand} ${cmd}`, { stdio: 'ignore' });
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
if (os.platform() === 'win32') {
|
|
79
|
+
try {
|
|
80
|
+
execSync(`${checkCommand} ${cmd}.exe`, { stdio: 'ignore' });
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
let command = '';
|
|
91
|
+
if (['1', 'true'].includes(geminiSandbox)) {
|
|
92
|
+
if (commandExists('docker')) {
|
|
93
|
+
command = 'docker';
|
|
94
|
+
} else if (commandExists('podman')) {
|
|
95
|
+
command = 'podman';
|
|
96
|
+
} else {
|
|
97
|
+
console.error(
|
|
98
|
+
'ERROR: install docker or podman or specify command in GEMINI_SANDBOX',
|
|
99
|
+
);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
} else if (geminiSandbox && !['0', 'false'].includes(geminiSandbox)) {
|
|
103
|
+
if (commandExists(geminiSandbox)) {
|
|
104
|
+
command = geminiSandbox;
|
|
105
|
+
} else {
|
|
106
|
+
console.error(
|
|
107
|
+
`ERROR: missing sandbox command '${geminiSandbox}' (from GEMINI_SANDBOX)`,
|
|
108
|
+
);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
if (os.platform() === 'darwin' && process.env.SEATBELT_PROFILE !== 'none') {
|
|
113
|
+
if (commandExists('sandbox-exec')) {
|
|
114
|
+
command = 'sandbox-exec';
|
|
115
|
+
} else {
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!argv.q) {
|
|
124
|
+
console.log(command);
|
|
125
|
+
}
|
|
126
|
+
process.exit(0);
|
package/scripts/start.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
// you may not use this file except in compliance with the License.
|
|
10
|
+
// You may obtain a copy of the License at
|
|
11
|
+
//
|
|
12
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
//
|
|
14
|
+
// Unless required by applicable law_or_agreed to in writing, software
|
|
15
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
// See the License for the specific language governing permissions and
|
|
18
|
+
// limitations under the License.
|
|
19
|
+
|
|
20
|
+
import { spawn, execSync } from 'node:child_process';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const root = join(__dirname, '..');
|
|
27
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8'));
|
|
28
|
+
|
|
29
|
+
// check build status, write warnings to file for app to display if needed
|
|
30
|
+
execSync('node ./scripts/check-build-status.js', {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
cwd: root,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const nodeArgs = [];
|
|
36
|
+
let sandboxCommand = undefined;
|
|
37
|
+
try {
|
|
38
|
+
sandboxCommand = execSync('node scripts/sandbox_command.js', {
|
|
39
|
+
cwd: root,
|
|
40
|
+
})
|
|
41
|
+
.toString()
|
|
42
|
+
.trim();
|
|
43
|
+
} catch {
|
|
44
|
+
// ignore
|
|
45
|
+
}
|
|
46
|
+
// if debugging is enabled and sandboxing is disabled, use --inspect-brk flag
|
|
47
|
+
// note with sandboxing this flag is passed to the binary inside the sandbox
|
|
48
|
+
// inside sandbox SANDBOX should be set and sandbox_command.js should fail
|
|
49
|
+
if (process.env.DEBUG && !sandboxCommand) {
|
|
50
|
+
if (process.env.SANDBOX) {
|
|
51
|
+
const port = process.env.DEBUG_PORT || '9229';
|
|
52
|
+
nodeArgs.push(`--inspect-brk=0.0.0.0:${port}`);
|
|
53
|
+
} else {
|
|
54
|
+
nodeArgs.push('--inspect-brk');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
nodeArgs.push(join(root, 'packages', 'cli'));
|
|
59
|
+
nodeArgs.push(...process.argv.slice(2));
|
|
60
|
+
|
|
61
|
+
const env = {
|
|
62
|
+
...process.env,
|
|
63
|
+
CLI_VERSION: pkg.version,
|
|
64
|
+
DEV: 'true',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (process.env.DEBUG) {
|
|
68
|
+
// If this is not set, the debugger will pause on the outer process rather
|
|
69
|
+
// than the relaunched process making it harder to debug.
|
|
70
|
+
env.QWEN_CODE_NO_RELAUNCH = 'true';
|
|
71
|
+
}
|
|
72
|
+
// Use process.cwd() to inherit the working directory from launch.json cwd setting
|
|
73
|
+
// This allows debugging from a specific directory (e.g., .todo)
|
|
74
|
+
const workingDir = process.env.QWEN_WORKING_DIR || process.cwd();
|
|
75
|
+
const child = spawn('node', nodeArgs, {
|
|
76
|
+
stdio: 'inherit',
|
|
77
|
+
env,
|
|
78
|
+
cwd: workingDir,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on('close', (code) => {
|
|
82
|
+
process.exit(code);
|
|
83
|
+
});
|