@bbigbang/agent-node 0.1.0
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/agentHost.js +483 -0
- package/dist/appVersion.js +14 -0
- package/dist/assetCachePaths.js +35 -0
- package/dist/attachmentInput.js +588 -0
- package/dist/attachmentMaterializer.js +230 -0
- package/dist/bigbangCli.js +17 -0
- package/dist/bigbangMessageSendDetection.js +284 -0
- package/dist/builtinSkillRoots.js +54 -0
- package/dist/claudeConfig.js +32 -0
- package/dist/claudeDirectRuntime.js +1960 -0
- package/dist/claudeSessionControls.js +78 -0
- package/dist/claudeTranscriptFs.js +147 -0
- package/dist/codexAppServerClient.js +188 -0
- package/dist/codexAppServerEnv.js +14 -0
- package/dist/codexAppServerRpc.js +273 -0
- package/dist/codexAppServerRuntime.js +3495 -0
- package/dist/codexBuiltinPrompt.js +117 -0
- package/dist/codexConversationSummarizer.js +76 -0
- package/dist/codexTranscriptFs.js +145 -0
- package/dist/config.js +129 -0
- package/dist/connection.js +151 -0
- package/dist/dispatchQueueStore.js +39 -0
- package/dist/dreamEnv.js +1 -0
- package/dist/dreamMemoryFallback.js +118 -0
- package/dist/dreamToolPolicy.js +293 -0
- package/dist/droidMissionRunner.js +808 -0
- package/dist/executor.js +1078 -0
- package/dist/hostRuntime.js +1 -0
- package/dist/libraryAuthorityFs.js +74 -0
- package/dist/libraryMirror.js +183 -0
- package/dist/main.js +1659 -0
- package/dist/native-worker/native-worker.mjs +475 -0
- package/dist/nativeMissionAgentDispatch.js +463 -0
- package/dist/nativeMissionRunner.js +461 -0
- package/dist/nativeSkillMounts.js +204 -0
- package/dist/nativeWorkerHost.js +142 -0
- package/dist/nodeSink.js +142 -0
- package/dist/panelHttpFetch.js +334 -0
- package/dist/runtimeDrivers.js +62 -0
- package/dist/skillFs.js +229 -0
- package/dist/soloHost.js +165 -0
- package/dist/soloNodeSink.js +138 -0
- package/dist/terminalManager.js +254 -0
- package/dist/workspaceFs.js +1020 -0
- package/dist/workspaceGit.js +694 -0
- package/dist/workspaceInspect.js +22 -0
- package/package.json +49 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { extractMaxDreamMessageSeq } from '@bbigbang/memory';
|
|
5
|
+
import { resolveBigbangCliEntry } from './bigbangCli.js';
|
|
6
|
+
export function resolveDreamShellCommand(command, env) {
|
|
7
|
+
if (!command.trimStart().startsWith('bigbang ')) {
|
|
8
|
+
return command;
|
|
9
|
+
}
|
|
10
|
+
const bigbangBin = env.BIGBANG_BIGBANG_BIN?.trim();
|
|
11
|
+
if (bigbangBin && existsSync(bigbangBin)) {
|
|
12
|
+
return command.replace(/^bigbang\b/, `"${bigbangBin.replace(/"/g, '\\"')}"`);
|
|
13
|
+
}
|
|
14
|
+
const entry = resolveBigbangCliEntry();
|
|
15
|
+
if (entry) {
|
|
16
|
+
return command.replace(/^bigbang\b/, `"${process.execPath}" "${entry.replace(/"/g, '\\"')}"`);
|
|
17
|
+
}
|
|
18
|
+
return command;
|
|
19
|
+
}
|
|
20
|
+
export function readDreamAuthTokenFromEnv(env) {
|
|
21
|
+
const inlineToken = env.BIGBANG_AUTH_TOKEN?.trim();
|
|
22
|
+
if (inlineToken)
|
|
23
|
+
return inlineToken;
|
|
24
|
+
const tokenFile = env.BIGBANG_AUTH_TOKEN_FILE?.trim();
|
|
25
|
+
if (!tokenFile || !existsSync(tokenFile))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
const token = readFileSync(tokenFile, 'utf8').trim();
|
|
29
|
+
return token || null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function resolveDreamShellEnv(env, cachedAuthToken) {
|
|
36
|
+
const resolved = { ...env };
|
|
37
|
+
const tokenFile = resolved.BIGBANG_AUTH_TOKEN_FILE?.trim();
|
|
38
|
+
const inlineToken = resolved.BIGBANG_AUTH_TOKEN?.trim() || cachedAuthToken?.trim() || null;
|
|
39
|
+
if (tokenFile && !existsSync(tokenFile)) {
|
|
40
|
+
delete resolved.BIGBANG_AUTH_TOKEN_FILE;
|
|
41
|
+
}
|
|
42
|
+
if (inlineToken) {
|
|
43
|
+
resolved.BIGBANG_AUTH_TOKEN = inlineToken;
|
|
44
|
+
}
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
async function runShellCommand(command, cwd, env, cachedAuthToken) {
|
|
48
|
+
const resolvedEnv = resolveDreamShellEnv(env, cachedAuthToken);
|
|
49
|
+
const resolvedCommand = resolveDreamShellCommand(command, resolvedEnv);
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const child = spawn(resolvedCommand, {
|
|
52
|
+
cwd,
|
|
53
|
+
env: { ...process.env, ...resolvedEnv },
|
|
54
|
+
shell: true,
|
|
55
|
+
});
|
|
56
|
+
let stdout = '';
|
|
57
|
+
let stderr = '';
|
|
58
|
+
child.stdout?.on('data', (chunk) => { stdout += String(chunk); });
|
|
59
|
+
child.stderr?.on('data', (chunk) => { stderr += String(chunk); });
|
|
60
|
+
child.on('close', (exitCode) => {
|
|
61
|
+
resolve({ exitCode, stdout, stderr });
|
|
62
|
+
});
|
|
63
|
+
child.on('error', (error) => {
|
|
64
|
+
resolve({ exitCode: 1, stdout, stderr: `${stderr}\n${String(error)}`.trim() });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function emitBashToolEvent(sink, toolCallId, command, stage, output, ok) {
|
|
69
|
+
await sink?.sendUi?.({
|
|
70
|
+
kind: 'tool',
|
|
71
|
+
mode: 'summary',
|
|
72
|
+
title: 'Bash',
|
|
73
|
+
input: { command, description: 'Dream memory fallback' },
|
|
74
|
+
toolCallId,
|
|
75
|
+
stage,
|
|
76
|
+
...(stage === 'complete'
|
|
77
|
+
? {
|
|
78
|
+
output: output ?? (ok ? 'ok' : 'failed'),
|
|
79
|
+
status: ok ? 'completed' : 'failed',
|
|
80
|
+
}
|
|
81
|
+
: {}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export async function executeDreamMemoryConsolidationFallback(params) {
|
|
85
|
+
const seq = extractMaxDreamMessageSeq(params.contextText ?? undefined);
|
|
86
|
+
const topic = `dream-fallback-${randomUUID().slice(0, 8)}`;
|
|
87
|
+
const createCommand = `bigbang memory node create --topic ${topic} --summary "Dream fallback memory write" --category project_knowledge --status draft`;
|
|
88
|
+
const createToolCallId = `dream-create-fallback-${randomUUID()}`;
|
|
89
|
+
await emitBashToolEvent(params.sink, createToolCallId, createCommand, 'start');
|
|
90
|
+
const createResult = await runShellCommand(createCommand, params.workspaceRoot, params.env, params.authToken);
|
|
91
|
+
const createOutput = [createResult.stdout, createResult.stderr].filter(Boolean).join('\n').trim();
|
|
92
|
+
const wroteMemory = createResult.exitCode === 0;
|
|
93
|
+
await emitBashToolEvent(params.sink, createToolCallId, createCommand, 'complete', createOutput, wroteMemory);
|
|
94
|
+
if (!wroteMemory || seq == null) {
|
|
95
|
+
return { wroteMemory, advancedWatermark: false };
|
|
96
|
+
}
|
|
97
|
+
const watermarkCommand = `bigbang memory dream-watermark --seq ${seq}`;
|
|
98
|
+
const watermarkToolCallId = `dream-watermark-fallback-${randomUUID()}`;
|
|
99
|
+
await emitBashToolEvent(params.sink, watermarkToolCallId, watermarkCommand, 'start');
|
|
100
|
+
const watermarkResult = await runShellCommand(watermarkCommand, params.workspaceRoot, params.env, params.authToken);
|
|
101
|
+
const watermarkOutput = [watermarkResult.stdout, watermarkResult.stderr].filter(Boolean).join('\n').trim();
|
|
102
|
+
const advancedWatermark = watermarkResult.exitCode === 0;
|
|
103
|
+
await emitBashToolEvent(params.sink, watermarkToolCallId, watermarkCommand, 'complete', watermarkOutput, advancedWatermark);
|
|
104
|
+
return { wroteMemory, advancedWatermark };
|
|
105
|
+
}
|
|
106
|
+
export async function executeDreamWatermarkFallback(params) {
|
|
107
|
+
const seq = extractMaxDreamMessageSeq(params.contextText ?? undefined);
|
|
108
|
+
if (seq == null)
|
|
109
|
+
return false;
|
|
110
|
+
const command = `bigbang memory dream-watermark --seq ${seq}`;
|
|
111
|
+
const toolCallId = `dream-watermark-fallback-${randomUUID()}`;
|
|
112
|
+
await emitBashToolEvent(params.sink, toolCallId, command, 'start');
|
|
113
|
+
const result = await runShellCommand(command, params.workspaceRoot, params.env, params.authToken);
|
|
114
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
115
|
+
const ok = result.exitCode === 0;
|
|
116
|
+
await emitBashToolEvent(params.sink, toolCallId, command, 'complete', output || (ok ? 'dream-watermark advanced' : 'dream-watermark failed'), ok);
|
|
117
|
+
return ok;
|
|
118
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const DREAM_MEMORY_WRITE_SUBCOMMANDS = new Set([
|
|
2
|
+
'create',
|
|
3
|
+
'update',
|
|
4
|
+
'dream-watermark',
|
|
5
|
+
]);
|
|
6
|
+
const DREAM_MEMORY_READ_SUBCOMMANDS = new Set([
|
|
7
|
+
'list',
|
|
8
|
+
'search',
|
|
9
|
+
]);
|
|
10
|
+
const DREAM_BLOCKED_MEMORY_MCP_MARKERS = [
|
|
11
|
+
'clude-memory',
|
|
12
|
+
'clude_memory',
|
|
13
|
+
];
|
|
14
|
+
export function extractShellCommand(input) {
|
|
15
|
+
if (!input || typeof input !== 'object')
|
|
16
|
+
return null;
|
|
17
|
+
const command = input.command;
|
|
18
|
+
return typeof command === 'string' && command.trim() ? command : null;
|
|
19
|
+
}
|
|
20
|
+
export function containsBigbangMemoryInvocation(command) {
|
|
21
|
+
return splitShellCommandSegments(command)
|
|
22
|
+
.some((segment) => containsBigbangMemoryAtCommandHead(segment));
|
|
23
|
+
}
|
|
24
|
+
function isHelpOnlyShellSegment(segment) {
|
|
25
|
+
return /\b--help\b/.test(segment) || /\bhelp\b/.test(segment.split(/\s+/).slice(-1)[0] ?? '');
|
|
26
|
+
}
|
|
27
|
+
function isPermittedDreamMemoryWriteSegment(segment) {
|
|
28
|
+
if (!segment.trim())
|
|
29
|
+
return false;
|
|
30
|
+
if (hasUnsafeShellSyntax(segment))
|
|
31
|
+
return false;
|
|
32
|
+
if (isHelpOnlyShellSegment(segment))
|
|
33
|
+
return false;
|
|
34
|
+
const invocation = parseBigbangMemoryInvocation(segment);
|
|
35
|
+
if (!invocation)
|
|
36
|
+
return false;
|
|
37
|
+
if (invocation.group === 'edge' && invocation.action === 'create')
|
|
38
|
+
return true;
|
|
39
|
+
if (invocation.group === 'node' && DREAM_MEMORY_WRITE_SUBCOMMANDS.has(invocation.action))
|
|
40
|
+
return true;
|
|
41
|
+
if (invocation.group === 'dream-watermark' && /--seq\b/.test(segment))
|
|
42
|
+
return true;
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
export function containsBigbangMemoryWriteInvocation(command) {
|
|
46
|
+
return splitShellCommandSegments(command).some((segment) => isPermittedDreamMemoryWriteSegment(segment));
|
|
47
|
+
}
|
|
48
|
+
function isOnlyDreamMemoryWriteCommand(command) {
|
|
49
|
+
const segments = splitShellCommandSegments(command).filter((segment) => segment.trim());
|
|
50
|
+
if (segments.length === 0)
|
|
51
|
+
return false;
|
|
52
|
+
return segments.every((segment) => isPermittedDreamMemoryWriteSegment(segment));
|
|
53
|
+
}
|
|
54
|
+
export function containsBigbangMemoryReadInvocation(command) {
|
|
55
|
+
return splitShellCommandSegments(command)
|
|
56
|
+
.some((segment) => {
|
|
57
|
+
const invocation = parseBigbangMemoryInvocation(segment);
|
|
58
|
+
if (!invocation)
|
|
59
|
+
return false;
|
|
60
|
+
if (invocation.group === 'node' && DREAM_MEMORY_READ_SUBCOMMANDS.has(invocation.action))
|
|
61
|
+
return true;
|
|
62
|
+
if (invocation.group === 'edge' && invocation.action === 'list')
|
|
63
|
+
return true;
|
|
64
|
+
if (invocation.group === 'search')
|
|
65
|
+
return true;
|
|
66
|
+
return false;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function isPermittedDreamMemoryWatermarkSegment(segment) {
|
|
70
|
+
if (!segment.trim())
|
|
71
|
+
return false;
|
|
72
|
+
if (hasUnsafeShellSyntax(segment))
|
|
73
|
+
return false;
|
|
74
|
+
if (isHelpOnlyShellSegment(segment))
|
|
75
|
+
return false;
|
|
76
|
+
const invocation = parseBigbangMemoryInvocation(segment);
|
|
77
|
+
return invocation?.group === 'dream-watermark' && /--seq\b/.test(segment);
|
|
78
|
+
}
|
|
79
|
+
export function isDreamMemoryWatermarkTool(toolName, input) {
|
|
80
|
+
const normalized = toolName.trim().toLowerCase();
|
|
81
|
+
if (normalized !== 'bash' && normalized !== 'shell')
|
|
82
|
+
return false;
|
|
83
|
+
const command = extractShellCommand(input);
|
|
84
|
+
if (!command)
|
|
85
|
+
return false;
|
|
86
|
+
const segments = splitShellCommandSegments(command).filter((segment) => segment.trim());
|
|
87
|
+
if (segments.length === 0)
|
|
88
|
+
return false;
|
|
89
|
+
return segments.every((segment) => isPermittedDreamMemoryWatermarkSegment(segment));
|
|
90
|
+
}
|
|
91
|
+
export function isDreamMemoryWriteTool(toolName, input) {
|
|
92
|
+
const normalized = toolName.trim().toLowerCase();
|
|
93
|
+
if (normalized !== 'bash' && normalized !== 'shell')
|
|
94
|
+
return false;
|
|
95
|
+
const command = extractShellCommand(input);
|
|
96
|
+
if (!command)
|
|
97
|
+
return false;
|
|
98
|
+
return isOnlyDreamMemoryWriteCommand(command);
|
|
99
|
+
}
|
|
100
|
+
export function isDreamMemoryReadTool(toolName, input) {
|
|
101
|
+
const normalized = toolName.trim().toLowerCase();
|
|
102
|
+
if (normalized !== 'bash' && normalized !== 'shell')
|
|
103
|
+
return false;
|
|
104
|
+
const command = extractShellCommand(input);
|
|
105
|
+
if (!command)
|
|
106
|
+
return false;
|
|
107
|
+
return containsBigbangMemoryReadInvocation(command);
|
|
108
|
+
}
|
|
109
|
+
export function isDreamBlockedMemoryTool(toolName) {
|
|
110
|
+
const normalized = toolName.trim().toLowerCase();
|
|
111
|
+
if (!normalized.startsWith('mcp__'))
|
|
112
|
+
return false;
|
|
113
|
+
if (DREAM_BLOCKED_MEMORY_MCP_MARKERS.some((marker) => normalized.includes(marker)))
|
|
114
|
+
return true;
|
|
115
|
+
if (normalized.includes('memory') && !normalized.includes('bigbang'))
|
|
116
|
+
return true;
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
export function buildDreamAdditionalDisallowedTools() {
|
|
120
|
+
return [
|
|
121
|
+
'mcp__clude-memory__get_memory_stats',
|
|
122
|
+
'mcp__clude-memory__search_memory',
|
|
123
|
+
'mcp__clude-memory__read_memory',
|
|
124
|
+
'Read',
|
|
125
|
+
'Grep',
|
|
126
|
+
'Glob',
|
|
127
|
+
'LS',
|
|
128
|
+
'WebFetch',
|
|
129
|
+
'WebSearch',
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
export function isDreamAllowedReadPath(input) {
|
|
133
|
+
if (!input || typeof input !== 'object')
|
|
134
|
+
return false;
|
|
135
|
+
const candidate = input.file_path
|
|
136
|
+
?? input.path;
|
|
137
|
+
if (typeof candidate !== 'string' || !candidate.trim())
|
|
138
|
+
return false;
|
|
139
|
+
const normalized = candidate.replace(/\\/g, '/').replace(/\/+/g, '/').toLowerCase();
|
|
140
|
+
const parts = normalized.split('/').filter((part) => part && part !== '.');
|
|
141
|
+
if (parts.includes('..'))
|
|
142
|
+
return false;
|
|
143
|
+
return normalized.endsWith('/memory.md')
|
|
144
|
+
|| normalized === 'memory.md'
|
|
145
|
+
|| parts.includes('notes');
|
|
146
|
+
}
|
|
147
|
+
export function evaluateDreamToolPermission(params) {
|
|
148
|
+
if (isDreamBlockedMemoryTool(params.toolName))
|
|
149
|
+
return 'deny_blocked_memory';
|
|
150
|
+
if (isDreamMemoryWriteTool(params.toolName, params.input))
|
|
151
|
+
return 'allow';
|
|
152
|
+
if (isDreamMemoryReadTool(params.toolName, params.input))
|
|
153
|
+
return 'deny_memory_read';
|
|
154
|
+
if (params.toolKind === 'read' && isDreamAllowedReadPath(params.input))
|
|
155
|
+
return 'allow';
|
|
156
|
+
if (params.toolKind === 'read' || params.toolKind === 'search' || params.toolKind === 'fetch') {
|
|
157
|
+
return 'deny_explore';
|
|
158
|
+
}
|
|
159
|
+
if (params.toolKind === 'execute')
|
|
160
|
+
return 'deny_execute';
|
|
161
|
+
return 'deny_explore';
|
|
162
|
+
}
|
|
163
|
+
function parseBigbangMemoryInvocation(segment) {
|
|
164
|
+
const tokens = shellUnquotedTokens(segment);
|
|
165
|
+
let index = 0;
|
|
166
|
+
while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[index])) {
|
|
167
|
+
index += 1;
|
|
168
|
+
}
|
|
169
|
+
if (tokens[index] === 'command' || tokens[index] === 'exec')
|
|
170
|
+
index += 1;
|
|
171
|
+
if (tokens[index] === 'env') {
|
|
172
|
+
index += 1;
|
|
173
|
+
while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[index])) {
|
|
174
|
+
index += 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (tokens[index] !== 'bigbang' || tokens[index + 1] !== 'memory')
|
|
178
|
+
return null;
|
|
179
|
+
const groupToken = tokens[index + 2];
|
|
180
|
+
if (groupToken === 'dream-watermark') {
|
|
181
|
+
return { group: 'dream-watermark', action: 'set' };
|
|
182
|
+
}
|
|
183
|
+
if (groupToken === 'search') {
|
|
184
|
+
return { group: 'search', action: 'search' };
|
|
185
|
+
}
|
|
186
|
+
if (groupToken === 'node' || groupToken === 'edge') {
|
|
187
|
+
const action = tokens[index + 3];
|
|
188
|
+
if (!action)
|
|
189
|
+
return null;
|
|
190
|
+
return { group: groupToken, action };
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
function containsBigbangMemoryAtCommandHead(segment) {
|
|
195
|
+
return parseBigbangMemoryInvocation(segment) != null;
|
|
196
|
+
}
|
|
197
|
+
function splitShellCommandSegments(value) {
|
|
198
|
+
const segments = [];
|
|
199
|
+
let current = '';
|
|
200
|
+
let quote = null;
|
|
201
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
202
|
+
const char = value[index];
|
|
203
|
+
const next = value[index + 1];
|
|
204
|
+
if (quote) {
|
|
205
|
+
current += char;
|
|
206
|
+
if (char === quote && value[index - 1] !== '\\')
|
|
207
|
+
quote = null;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (char === '"' || char === "'") {
|
|
211
|
+
quote = char;
|
|
212
|
+
current += char;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (char === ';'
|
|
216
|
+
|| char === '|'
|
|
217
|
+
|| char === '<'
|
|
218
|
+
|| char === '>'
|
|
219
|
+
|| char === '&'
|
|
220
|
+
|| char === '\n'
|
|
221
|
+
|| char === '\r') {
|
|
222
|
+
if (current.trim())
|
|
223
|
+
segments.push(current);
|
|
224
|
+
current = '';
|
|
225
|
+
if ((char === '&' && next === '&') || (char === '|' && next === '|'))
|
|
226
|
+
index += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
current += char;
|
|
230
|
+
}
|
|
231
|
+
if (current.trim())
|
|
232
|
+
segments.push(current);
|
|
233
|
+
return segments;
|
|
234
|
+
}
|
|
235
|
+
function hasUnsafeShellSyntax(command) {
|
|
236
|
+
let quote = null;
|
|
237
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
238
|
+
const char = command[index];
|
|
239
|
+
const next = command[index + 1];
|
|
240
|
+
if (quote) {
|
|
241
|
+
if (char === quote && command[index - 1] !== '\\')
|
|
242
|
+
quote = null;
|
|
243
|
+
if (quote === '"' && ((char === '$' && next === '(') || char === '`'))
|
|
244
|
+
return true;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (char === '"' || char === "'") {
|
|
248
|
+
quote = char;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (char === '|' || char === '<' || char === '>' || char === '&' || char === '`')
|
|
252
|
+
return true;
|
|
253
|
+
if (char === '\n' || char === '\r')
|
|
254
|
+
return true;
|
|
255
|
+
if (char === '$' && next === '(')
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
return quote != null;
|
|
259
|
+
}
|
|
260
|
+
function shellUnquotedTokens(command) {
|
|
261
|
+
const tokens = [];
|
|
262
|
+
let current = '';
|
|
263
|
+
let quote = null;
|
|
264
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
265
|
+
const char = command[index];
|
|
266
|
+
if (quote) {
|
|
267
|
+
if (char === quote && command[index - 1] !== '\\') {
|
|
268
|
+
if (current)
|
|
269
|
+
tokens.push(current);
|
|
270
|
+
current = '';
|
|
271
|
+
quote = null;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
current += char;
|
|
275
|
+
}
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (char === '"' || char === "'") {
|
|
279
|
+
quote = char;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (/\s/.test(char)) {
|
|
283
|
+
if (current)
|
|
284
|
+
tokens.push(current);
|
|
285
|
+
current = '';
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
current += char;
|
|
289
|
+
}
|
|
290
|
+
if (current)
|
|
291
|
+
tokens.push(current);
|
|
292
|
+
return tokens;
|
|
293
|
+
}
|