mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Fix non-interactive init to complete with piped stdin
This commit is contained in:
1 parent
1fa4d4560e
commit
85f9d3c4e7
2 files changed
+76
-15
No files matched your search
+32
-15
@@ -186,35 +186,52 @@ function syncAppFiles(torchDir, installDir, verb = 'Copied') {
|
||||
* @returns {Promise<{installDir: string, namespace: string, hashtag: string, relays: string[]}>} - User configuration.
|
||||
*/
|
||||
async function interactiveInit(cwd) {
|
||||
const rl = readline.createInterface({ input, output });
|
||||
const currentDirName = path.basename(cwd);
|
||||
let defaultDir = 'torch';
|
||||
if (currentDirName === 'torch') {
|
||||
defaultDir = '.';
|
||||
}
|
||||
const randomSuffix = crypto.randomBytes(4).toString('hex');
|
||||
const defaultNamespace = `torch-${randomSuffix}`;
|
||||
const defaultHashtag = `${defaultNamespace}-agent-lock`;
|
||||
|
||||
if (!input.isTTY) {
|
||||
return {
|
||||
installDir: defaultDir,
|
||||
namespace: defaultNamespace,
|
||||
hashtag: defaultHashtag,
|
||||
relays: DEFAULT_RELAYS,
|
||||
};
|
||||
}
|
||||
|
||||
const rl = readline.createInterface({ input, output });
|
||||
const askWithDefault = async (prompt, fallback) => {
|
||||
try {
|
||||
const answer = await rl.question(prompt);
|
||||
return answer.trim() || fallback;
|
||||
} catch {
|
||||
// In non-interactive/piped sessions EOF can arrive mid-flow.
|
||||
// Treat it as "accept defaults" so init still completes deterministically.
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
console.log('\n🔥 TORCH Initialization 🔥\n');
|
||||
|
||||
try {
|
||||
// 1. Install Directory
|
||||
let defaultDir = 'torch';
|
||||
if (currentDirName === 'torch') {
|
||||
defaultDir = '.';
|
||||
}
|
||||
|
||||
const dirAnswer = await rl.question(`Install directory (default: ${defaultDir}): `);
|
||||
const installDir = dirAnswer.trim() || defaultDir;
|
||||
const installDir = await askWithDefault(`Install directory (default: ${defaultDir}): `, defaultDir);
|
||||
|
||||
// 2. Namespace
|
||||
const randomSuffix = crypto.randomBytes(4).toString('hex');
|
||||
const defaultNamespace = `torch-${randomSuffix}`;
|
||||
const namespaceAnswer = await rl.question(`Nostr Namespace (default: ${defaultNamespace}): `);
|
||||
const namespace = namespaceAnswer.trim() || defaultNamespace;
|
||||
const namespace = await askWithDefault(`Nostr Namespace (default: ${defaultNamespace}): `, defaultNamespace);
|
||||
|
||||
// 3. Hashtag
|
||||
const defaultHashtag = `${namespace}-agent-lock`;
|
||||
const hashtagAnswer = await rl.question(`Nostr Hashtag (default: ${defaultHashtag}): `);
|
||||
const hashtag = hashtagAnswer.trim() || defaultHashtag;
|
||||
const hashtag = await askWithDefault(`Nostr Hashtag (default: ${defaultHashtag}): `, defaultHashtag);
|
||||
|
||||
// 4. Relays
|
||||
console.log(`\nDefault Relays:\n ${DEFAULT_RELAYS.join('\n ')}`);
|
||||
const relaysAnswer = await rl.question('Enter relays (comma-separated) or press Enter to use defaults: ');
|
||||
const relaysAnswer = await askWithDefault('Enter relays (comma-separated) or press Enter to use defaults: ', '');
|
||||
let relays = DEFAULT_RELAYS;
|
||||
if (relaysAnswer.trim()) {
|
||||
relays = relaysAnswer.split(',').map(r => r.trim()).filter(Boolean);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
function runInitWithSingleNewline(cwd) {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(process.execPath, [path.resolve('bin/torch-lock.mjs'), 'init', '--force'], {
|
||||
cwd,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
child.stdout.on('data', (chunk) => {
|
||||
stdout += chunk.toString();
|
||||
});
|
||||
child.stderr.on('data', (chunk) => {
|
||||
stderr += chunk.toString();
|
||||
});
|
||||
|
||||
child.stdin.write('\n');
|
||||
child.stdin.end();
|
||||
|
||||
child.on('close', (code) => {
|
||||
resolve({ code: code ?? 1, stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('init completes in non-interactive mode when stdin closes early', async () => {
|
||||
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'torch-init-noninteractive-'));
|
||||
await fs.promises.writeFile(path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'tmp-host', version: '1.0.0' }), 'utf8');
|
||||
|
||||
const result = await runInitWithSingleNewline(tmpDir);
|
||||
assert.equal(result.code, 0, `stdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
|
||||
|
||||
assert.ok(fs.existsSync(path.join(tmpDir, 'torch')), 'torch directory should be created');
|
||||
assert.ok(fs.existsSync(path.join(tmpDir, 'torch-config.json')), 'root torch-config.json should be created');
|
||||
assert.ok(fs.existsSync(path.join(tmpDir, 'torch', 'src', 'prompts', 'scheduler-flow.md')), 'scheduler flow should be installed');
|
||||
});
|
||||
Reference in new issue
Block a user