@hunterzhu/pulse-cli 0.1.0 → 0.1.2
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/dist/bin.js +10 -8
- package/dist/config.d.ts +4 -0
- package/dist/config.js +31 -2
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { access, chmod, mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
6
|
import { createLocalHost } from '@hunterzhu/pulse-server';
|
|
7
|
-
import { expandHome, loadPulseConfig } from './config.js';
|
|
8
|
-
const
|
|
7
|
+
import { defaultPulseConfig, defaultPulseConfigPath, ensurePulseUserConfig, expandHome, loadPulseConfig } from './config.js';
|
|
8
|
+
const packageManifest = createRequire(import.meta.url)('../package.json');
|
|
9
|
+
const version = packageManifest.version;
|
|
9
10
|
const help = `Pulse ${version}
|
|
10
11
|
|
|
11
12
|
Usage:
|
|
@@ -32,7 +33,7 @@ Options:
|
|
|
32
33
|
--no-color disable terminal styling
|
|
33
34
|
--help, -h show this help
|
|
34
35
|
--version, -v show the version
|
|
35
|
-
setup --force write a user config template
|
|
36
|
+
setup --force write a user config template (also created on first run)
|
|
36
37
|
`;
|
|
37
38
|
function parse(argv) {
|
|
38
39
|
const options = {};
|
|
@@ -79,8 +80,9 @@ function parse(argv) {
|
|
|
79
80
|
return { command, positionals, options };
|
|
80
81
|
}
|
|
81
82
|
function option(options, key) { const value = options[key]; return typeof value === 'string' ? value : undefined; }
|
|
82
|
-
async function hostOptions(parsed) { const requestedCwd = expandHome(option(parsed.options, 'cwd')); const
|
|
83
|
-
|
|
83
|
+
async function hostOptions(parsed) { const requestedCwd = expandHome(option(parsed.options, 'cwd')); const explicitConfig = expandHome(option(parsed.options, 'config')); if (explicitConfig === undefined && process.env.PULSE_CONFIG === undefined)
|
|
84
|
+
await ensurePulseUserConfig(); const config = (await loadPulseConfig(requestedCwd ?? process.cwd(), explicitConfig, parsed.options['trust-workspace'] === true)).value; const providerName = option(parsed.options, 'provider') ?? process.env.PULSE_PROVIDER ?? config.provider?.provider; const model = option(parsed.options, 'model') ?? process.env.PULSE_MODEL ?? config.provider?.model; const baseURL = option(parsed.options, 'base-url') ?? process.env.PULSE_BASE_URL ?? config.provider?.baseURL; const apiKeyEnv = config.provider?.apiKeyEnv ?? (providerName === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'); const apiKey = process.env[apiKeyEnv]; const provider = providerName ? { provider: providerName, ...(model === undefined ? {} : { defaultModel: model }), ...(baseURL === undefined ? {} : { baseURL }), ...(apiKey === undefined ? {} : { apiKey }) } : undefined; const cwd = requestedCwd ?? expandHome(config.cwd); const dataDir = expandHome(option(parsed.options, 'data-dir') ?? process.env.PULSE_DATA_DIR ?? config.dataDir); const mockResponse = option(parsed.options, 'mock-response'); const approvalMode = parsed.options['read-only'] === true ? 'read-only' : parsed.options['auto-approve'] === true || process.env.PULSE_AUTO_APPROVE === '1' ? 'auto' : config.approvalMode; const allowNetwork = parsed.options['allow-network'] === true || process.env.PULSE_ALLOW_NETWORK === '1' ? true : config.allowNetwork; return { ...(cwd === undefined ? {} : { cwd }), ...(dataDir === undefined ? {} : { dataDir }), ...(provider === undefined ? {} : { provider }), ...(mockResponse === undefined ? {} : { mockResponse }), ...(approvalMode === undefined ? {} : { approvalMode }), ...(allowNetwork === undefined ? {} : { allowNetwork }) }; }
|
|
85
|
+
async function setupConfig(force, explicitPath) { const path = explicitPath ?? process.env.PULSE_CONFIG ?? defaultPulseConfigPath(); try {
|
|
84
86
|
await access(path);
|
|
85
87
|
if (!force)
|
|
86
88
|
throw new Error(`CONFIG_EXISTS:${path}`);
|
|
@@ -88,7 +90,7 @@ async function setupConfig(force, explicitPath) { const path = explicitPath ?? p
|
|
|
88
90
|
catch (error) {
|
|
89
91
|
if (error.code !== 'ENOENT' && error instanceof Error && error.message.startsWith('CONFIG_EXISTS:'))
|
|
90
92
|
throw error;
|
|
91
|
-
} await mkdir(
|
|
93
|
+
} await mkdir(dirname(path), { recursive: true }); await writeFile(path, `${JSON.stringify(defaultPulseConfig, null, 2)}\n`, { mode: 0o600 }); await chmod(path, 0o600); process.stdout.write(`Wrote ${path}\n`); }
|
|
92
94
|
function writeEvent(event, format) { if (format === 'jsonl') {
|
|
93
95
|
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
94
96
|
return;
|
package/dist/config.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface PulseCliConfig {
|
|
|
10
10
|
approvalMode?: 'read-only' | 'ask' | 'auto';
|
|
11
11
|
allowNetwork?: boolean;
|
|
12
12
|
}
|
|
13
|
+
export declare const defaultPulseConfig: PulseCliConfig;
|
|
14
|
+
export declare function defaultPulseConfigPath(): string;
|
|
15
|
+
/** Create the user config on first run without replacing an existing file. */
|
|
16
|
+
export declare function ensurePulseUserConfig(path?: string): Promise<void>;
|
|
13
17
|
export declare function expandHome(path: string | undefined): string | undefined;
|
|
14
18
|
/** Workspace files must not escalate approval, network, or provider credentials. */
|
|
15
19
|
export declare function sanitizeWorkspaceConfig(value: PulseCliConfig): PulseCliConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
1
|
+
import { access, chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
export const defaultPulseConfig = {
|
|
5
|
+
provider: { provider: 'mock', model: 'mock', apiKeyEnv: 'OPENAI_API_KEY' },
|
|
6
|
+
approvalMode: 'ask',
|
|
7
|
+
allowNetwork: false,
|
|
8
|
+
};
|
|
9
|
+
export function defaultPulseConfigPath() {
|
|
10
|
+
return join(homedir(), '.pulse', 'config.json');
|
|
11
|
+
}
|
|
12
|
+
/** Create the user config on first run without replacing an existing file. */
|
|
13
|
+
export async function ensurePulseUserConfig(path = defaultPulseConfigPath()) {
|
|
14
|
+
await mkdir(dirname(path), { recursive: true });
|
|
15
|
+
try {
|
|
16
|
+
await access(path);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error.code !== 'ENOENT')
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await writeFile(path, `${JSON.stringify(defaultPulseConfig, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
// Another process may have initialized the config between access and write.
|
|
28
|
+
if (error.code !== 'EEXIST')
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
await chmod(path, 0o600);
|
|
32
|
+
}
|
|
4
33
|
export function expandHome(path) {
|
|
5
34
|
if (path === undefined)
|
|
6
35
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hunterzhu/pulse-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zhuhengtan/Pulse"
|
|
@@ -19,6 +19,6 @@
|
|
|
19
19
|
"build": "tsc -p tsconfig.json"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@hunterzhu/pulse-server": "0.1.
|
|
22
|
+
"@hunterzhu/pulse-server": "0.1.2"
|
|
23
23
|
}
|
|
24
24
|
}
|