@qodercn-ai/qoderclicn 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/LICENSE +202 -0
- package/README.md +153 -0
- package/bundle/builtin/agent-creator/SKILL.md +327 -0
- package/bundle/builtin/hook-config/SKILL.md +480 -0
- package/bundle/builtin/mcp-config/SKILL.md +155 -0
- package/bundle/builtin/skill-creator/SKILL.md +294 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js +101 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.d.ts +7 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js +9 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.d.ts +48 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js +323 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.d.ts +36 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js +7 -0
- package/bundle/node_modules/@google/gemini-cli-devtools/package.json +34 -0
- package/bundle/policies/sandbox-default.toml +19 -0
- package/bundle/qoderclicn.js +51 -0
- package/bundle/sandbox-macos-permissive-open.sb +27 -0
- package/bundle/sandbox-macos-permissive-proxied.sb +37 -0
- package/bundle/sandbox-macos-restrictive-open.sb +96 -0
- package/bundle/sandbox-macos-restrictive-proxied.sb +98 -0
- package/bundle/sandbox-macos-strict-open.sb +131 -0
- package/bundle/sandbox-macos-strict-proxied.sb +133 -0
- package/package.json +43 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
import { type WebSocket } from 'ws';
|
|
8
|
+
import type { NetworkLog, ConsoleLogPayload } from './types.js';
|
|
9
|
+
export type { NetworkLog, ConsoleLogPayload, InspectorConsoleLog, } from './types.js';
|
|
10
|
+
interface IncomingNetworkPayload extends Partial<NetworkLog> {
|
|
11
|
+
chunk?: {
|
|
12
|
+
index: number;
|
|
13
|
+
data: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface SessionInfo {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
ws: WebSocket;
|
|
20
|
+
lastPing: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* DevTools Viewer
|
|
24
|
+
*
|
|
25
|
+
* Receives logs via WebSocket from CLI sessions.
|
|
26
|
+
*/
|
|
27
|
+
export declare class DevTools extends EventEmitter {
|
|
28
|
+
private static instance;
|
|
29
|
+
private logs;
|
|
30
|
+
private consoleLogs;
|
|
31
|
+
private server;
|
|
32
|
+
private wss;
|
|
33
|
+
private sessions;
|
|
34
|
+
private heartbeatTimer;
|
|
35
|
+
private port;
|
|
36
|
+
private static readonly DEFAULT_PORT;
|
|
37
|
+
private static readonly MAX_PORT_RETRIES;
|
|
38
|
+
private constructor();
|
|
39
|
+
static getInstance(): DevTools;
|
|
40
|
+
addInternalConsoleLog(payload: ConsoleLogPayload, sessionId?: string, timestamp?: number): void;
|
|
41
|
+
addInternalNetworkLog(payload: IncomingNetworkPayload, sessionId?: string, timestamp?: number): void;
|
|
42
|
+
getUrl(): string;
|
|
43
|
+
getPort(): number;
|
|
44
|
+
stop(): Promise<void>;
|
|
45
|
+
start(): Promise<string>;
|
|
46
|
+
private setupWebSocketServer;
|
|
47
|
+
private handleWebSocketMessage;
|
|
48
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import http from 'node:http';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import { WebSocketServer } from 'ws';
|
|
10
|
+
import { INDEX_HTML, CLIENT_JS } from './_client-assets.js';
|
|
11
|
+
/**
|
|
12
|
+
* DevTools Viewer
|
|
13
|
+
*
|
|
14
|
+
* Receives logs via WebSocket from CLI sessions.
|
|
15
|
+
*/
|
|
16
|
+
export class DevTools extends EventEmitter {
|
|
17
|
+
static instance;
|
|
18
|
+
logs = [];
|
|
19
|
+
consoleLogs = [];
|
|
20
|
+
server = null;
|
|
21
|
+
wss = null;
|
|
22
|
+
sessions = new Map();
|
|
23
|
+
heartbeatTimer = null;
|
|
24
|
+
port = 25417;
|
|
25
|
+
static DEFAULT_PORT = 25417;
|
|
26
|
+
static MAX_PORT_RETRIES = 10;
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
// Each SSE client adds 3 listeners; raise the limit to avoid warnings
|
|
30
|
+
this.setMaxListeners(50);
|
|
31
|
+
}
|
|
32
|
+
static getInstance() {
|
|
33
|
+
if (!DevTools.instance) {
|
|
34
|
+
DevTools.instance = new DevTools();
|
|
35
|
+
}
|
|
36
|
+
return DevTools.instance;
|
|
37
|
+
}
|
|
38
|
+
addInternalConsoleLog(payload, sessionId, timestamp) {
|
|
39
|
+
const entry = {
|
|
40
|
+
...payload,
|
|
41
|
+
id: randomUUID(),
|
|
42
|
+
sessionId,
|
|
43
|
+
timestamp: timestamp || Date.now(),
|
|
44
|
+
};
|
|
45
|
+
this.consoleLogs.push(entry);
|
|
46
|
+
if (this.consoleLogs.length > 5000)
|
|
47
|
+
this.consoleLogs.shift();
|
|
48
|
+
this.emit('console-update', entry);
|
|
49
|
+
}
|
|
50
|
+
addInternalNetworkLog(payload, sessionId, timestamp) {
|
|
51
|
+
if (!payload.id)
|
|
52
|
+
return;
|
|
53
|
+
const existingIndex = this.logs.findIndex((l) => l.id === payload.id);
|
|
54
|
+
if (existingIndex > -1) {
|
|
55
|
+
const existing = this.logs[existingIndex];
|
|
56
|
+
// Handle chunk accumulation
|
|
57
|
+
if (payload.chunk) {
|
|
58
|
+
const chunks = existing.chunks || [];
|
|
59
|
+
chunks.push(payload.chunk);
|
|
60
|
+
this.logs[existingIndex] = {
|
|
61
|
+
...existing,
|
|
62
|
+
chunks,
|
|
63
|
+
sessionId: sessionId || existing.sessionId,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.logs[existingIndex] = {
|
|
68
|
+
...existing,
|
|
69
|
+
...payload,
|
|
70
|
+
sessionId: sessionId || existing.sessionId,
|
|
71
|
+
// Drop chunks once we have the full response body — the data
|
|
72
|
+
// is redundant and keeping both can blow past V8's string limit
|
|
73
|
+
// when serializing the snapshot.
|
|
74
|
+
chunks: payload.response?.body ? undefined : existing.chunks,
|
|
75
|
+
response: payload.response
|
|
76
|
+
? { ...existing.response, ...payload.response }
|
|
77
|
+
: existing.response,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
this.emit('update', this.logs[existingIndex]);
|
|
81
|
+
}
|
|
82
|
+
else if (payload.url) {
|
|
83
|
+
const entry = {
|
|
84
|
+
...payload,
|
|
85
|
+
sessionId,
|
|
86
|
+
timestamp: timestamp || Date.now(),
|
|
87
|
+
chunks: payload.chunk ? [payload.chunk] : undefined,
|
|
88
|
+
};
|
|
89
|
+
this.logs.push(entry);
|
|
90
|
+
if (this.logs.length > 2000)
|
|
91
|
+
this.logs.shift();
|
|
92
|
+
this.emit('update', entry);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
getUrl() {
|
|
96
|
+
return `http://127.0.0.1:${this.port}`;
|
|
97
|
+
}
|
|
98
|
+
getPort() {
|
|
99
|
+
return this.port;
|
|
100
|
+
}
|
|
101
|
+
stop() {
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
if (this.heartbeatTimer) {
|
|
104
|
+
clearInterval(this.heartbeatTimer);
|
|
105
|
+
this.heartbeatTimer = null;
|
|
106
|
+
}
|
|
107
|
+
if (this.wss) {
|
|
108
|
+
this.wss.close();
|
|
109
|
+
this.wss = null;
|
|
110
|
+
}
|
|
111
|
+
if (this.server) {
|
|
112
|
+
this.server.close(() => resolve());
|
|
113
|
+
this.server = null;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
resolve();
|
|
117
|
+
}
|
|
118
|
+
// Reset singleton so a fresh start() is possible
|
|
119
|
+
DevTools.instance = undefined;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
start() {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
if (this.server) {
|
|
125
|
+
resolve(this.getUrl());
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.server = http.createServer((req, res) => {
|
|
129
|
+
// Only allow same-origin requests — the client is served from this
|
|
130
|
+
// server so cross-origin access is unnecessary and would let arbitrary
|
|
131
|
+
// websites exfiltrate logs (which may contain API keys/headers).
|
|
132
|
+
const origin = req.headers.origin;
|
|
133
|
+
if (origin) {
|
|
134
|
+
const allowed = `http://127.0.0.1:${this.port}`;
|
|
135
|
+
if (origin === allowed) {
|
|
136
|
+
res.setHeader('Access-Control-Allow-Origin', allowed);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// API routes
|
|
140
|
+
if (req.url === '/api/trigger-debugger' && req.method === 'POST') {
|
|
141
|
+
let body = '';
|
|
142
|
+
req.on('data', (chunk) => {
|
|
143
|
+
body += chunk;
|
|
144
|
+
});
|
|
145
|
+
req.on('end', () => {
|
|
146
|
+
try {
|
|
147
|
+
const parsed = JSON.parse(body);
|
|
148
|
+
if (typeof parsed !== 'object' ||
|
|
149
|
+
parsed === null ||
|
|
150
|
+
!('sessionId' in parsed) ||
|
|
151
|
+
typeof parsed.sessionId !== 'string') {
|
|
152
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
153
|
+
res.end(JSON.stringify({ error: 'Invalid request' }));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const sessionId = parsed.sessionId;
|
|
157
|
+
const session = this.sessions.get(sessionId);
|
|
158
|
+
if (session) {
|
|
159
|
+
session.ws.send(JSON.stringify({ type: 'trigger-debugger' }));
|
|
160
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
161
|
+
res.end(JSON.stringify({ success: true }));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
165
|
+
res.end(JSON.stringify({ error: 'Session not found' }));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
170
|
+
res.end(JSON.stringify({ error: 'Invalid request' }));
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
else if (req.url === '/events') {
|
|
175
|
+
res.writeHead(200, {
|
|
176
|
+
'Content-Type': 'text/event-stream',
|
|
177
|
+
'Cache-Control': 'no-cache',
|
|
178
|
+
Connection: 'keep-alive',
|
|
179
|
+
});
|
|
180
|
+
// Send full snapshot on connect
|
|
181
|
+
const snapshot = JSON.stringify({
|
|
182
|
+
networkLogs: this.logs,
|
|
183
|
+
consoleLogs: this.consoleLogs,
|
|
184
|
+
sessions: Array.from(this.sessions.keys()),
|
|
185
|
+
});
|
|
186
|
+
res.write(`event: snapshot\ndata: ${snapshot}\n\n`);
|
|
187
|
+
// Incremental updates
|
|
188
|
+
const onNetwork = (log) => {
|
|
189
|
+
res.write(`event: network\ndata: ${JSON.stringify(log)}\n\n`);
|
|
190
|
+
};
|
|
191
|
+
const onConsole = (log) => {
|
|
192
|
+
res.write(`event: console\ndata: ${JSON.stringify(log)}\n\n`);
|
|
193
|
+
};
|
|
194
|
+
const onSession = () => {
|
|
195
|
+
const sessions = Array.from(this.sessions.keys());
|
|
196
|
+
res.write(`event: session\ndata: ${JSON.stringify(sessions)}\n\n`);
|
|
197
|
+
};
|
|
198
|
+
this.on('update', onNetwork);
|
|
199
|
+
this.on('console-update', onConsole);
|
|
200
|
+
this.on('session-update', onSession);
|
|
201
|
+
req.on('close', () => {
|
|
202
|
+
this.off('update', onNetwork);
|
|
203
|
+
this.off('console-update', onConsole);
|
|
204
|
+
this.off('session-update', onSession);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else if (req.url === '/' || req.url === '/index.html') {
|
|
208
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
209
|
+
res.end(INDEX_HTML);
|
|
210
|
+
}
|
|
211
|
+
else if (req.url === '/assets/main.js') {
|
|
212
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
213
|
+
res.end(CLIENT_JS);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
res.writeHead(404);
|
|
217
|
+
res.end('Not Found');
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
this.server.on('error', (e) => {
|
|
221
|
+
if (typeof e === 'object' &&
|
|
222
|
+
e !== null &&
|
|
223
|
+
'code' in e &&
|
|
224
|
+
e.code === 'EADDRINUSE') {
|
|
225
|
+
if (this.port - DevTools.DEFAULT_PORT >= DevTools.MAX_PORT_RETRIES) {
|
|
226
|
+
reject(new Error(`DevTools: all ports ${DevTools.DEFAULT_PORT}–${this.port} in use`));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
this.port++;
|
|
230
|
+
this.server?.listen(this.port, '127.0.0.1');
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
this.server.listen(this.port, '127.0.0.1', () => {
|
|
237
|
+
this.setupWebSocketServer();
|
|
238
|
+
resolve(this.getUrl());
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
setupWebSocketServer() {
|
|
243
|
+
if (!this.server)
|
|
244
|
+
return;
|
|
245
|
+
this.wss = new WebSocketServer({ server: this.server, path: '/ws' });
|
|
246
|
+
this.wss.on('connection', (ws) => {
|
|
247
|
+
let sessionId = null;
|
|
248
|
+
ws.on('message', (data) => {
|
|
249
|
+
try {
|
|
250
|
+
const message = JSON.parse(data.toString());
|
|
251
|
+
// Handle registration first
|
|
252
|
+
if (message.type === 'register') {
|
|
253
|
+
sessionId = String(message.sessionId);
|
|
254
|
+
if (!sessionId)
|
|
255
|
+
return;
|
|
256
|
+
this.sessions.set(sessionId, {
|
|
257
|
+
sessionId,
|
|
258
|
+
ws,
|
|
259
|
+
lastPing: Date.now(),
|
|
260
|
+
});
|
|
261
|
+
// Notify session update
|
|
262
|
+
this.emit('session-update');
|
|
263
|
+
// Send registration acknowledgement
|
|
264
|
+
ws.send(JSON.stringify({
|
|
265
|
+
type: 'registered',
|
|
266
|
+
sessionId,
|
|
267
|
+
timestamp: Date.now(),
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
else if (sessionId) {
|
|
271
|
+
this.handleWebSocketMessage(sessionId, message);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Invalid WebSocket message
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
ws.on('close', () => {
|
|
279
|
+
if (sessionId) {
|
|
280
|
+
this.sessions.delete(sessionId);
|
|
281
|
+
this.emit('session-update');
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
ws.on('error', () => {
|
|
285
|
+
// WebSocket error — no action needed
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
// Heartbeat mechanism
|
|
289
|
+
this.heartbeatTimer = setInterval(() => {
|
|
290
|
+
const now = Date.now();
|
|
291
|
+
this.sessions.forEach((session, sessionId) => {
|
|
292
|
+
if (now - session.lastPing > 30000) {
|
|
293
|
+
session.ws.close();
|
|
294
|
+
this.sessions.delete(sessionId);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
// Send ping
|
|
298
|
+
session.ws.send(JSON.stringify({ type: 'ping', timestamp: now }));
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}, 10000);
|
|
302
|
+
this.heartbeatTimer.unref();
|
|
303
|
+
}
|
|
304
|
+
handleWebSocketMessage(sessionId, message) {
|
|
305
|
+
const session = this.sessions.get(sessionId);
|
|
306
|
+
if (!session)
|
|
307
|
+
return;
|
|
308
|
+
switch (message['type']) {
|
|
309
|
+
case 'pong':
|
|
310
|
+
session.lastPing = Date.now();
|
|
311
|
+
break;
|
|
312
|
+
case 'console':
|
|
313
|
+
this.addInternalConsoleLog(message['payload'], sessionId, message['timestamp']);
|
|
314
|
+
break;
|
|
315
|
+
case 'network':
|
|
316
|
+
this.addInternalNetworkLog(message['payload'], sessionId, message['timestamp']);
|
|
317
|
+
break;
|
|
318
|
+
default:
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
export interface NetworkLog {
|
|
7
|
+
id: string;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
method: string;
|
|
11
|
+
url: string;
|
|
12
|
+
headers: Record<string, string | string[] | undefined>;
|
|
13
|
+
body?: string;
|
|
14
|
+
pending?: boolean;
|
|
15
|
+
chunks?: Array<{
|
|
16
|
+
index: number;
|
|
17
|
+
data: string;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}>;
|
|
20
|
+
response?: {
|
|
21
|
+
status: number;
|
|
22
|
+
headers: Record<string, string | string[] | undefined>;
|
|
23
|
+
body?: string;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
};
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ConsoleLogPayload {
|
|
29
|
+
type: 'log' | 'warn' | 'error' | 'debug' | 'info';
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
export interface InspectorConsoleLog extends ConsoleLogPayload {
|
|
33
|
+
id: string;
|
|
34
|
+
sessionId?: string;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@google/gemini-cli-devtools",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"default": "./dist/src/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "npm run build:client && tsc -p tsconfig.build.json",
|
|
16
|
+
"build:client": "node esbuild.client.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"client/index.html"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.11.24",
|
|
27
|
+
"react": "^19.2.0",
|
|
28
|
+
"react-dom": "^19.2.0",
|
|
29
|
+
"typescript": "^5.3.3"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"ws": "^8.16.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[modes.plan]
|
|
2
|
+
network = false
|
|
3
|
+
readonly = true
|
|
4
|
+
approvedTools = []
|
|
5
|
+
allowOverrides = true
|
|
6
|
+
|
|
7
|
+
[modes.default]
|
|
8
|
+
network = false
|
|
9
|
+
readonly = false
|
|
10
|
+
approvedTools = ['cat', 'ls', 'grep', 'head', 'tail', 'less', 'Get-Content', 'dir', 'type', 'findstr', 'Get-ChildItem', 'echo']
|
|
11
|
+
allowOverrides = true
|
|
12
|
+
|
|
13
|
+
[modes.accepting_edits]
|
|
14
|
+
network = false
|
|
15
|
+
readonly = false
|
|
16
|
+
approvedTools = ['sed', 'grep', 'awk', 'perl', 'cat', 'echo', 'Add-Content', 'Set-Content']
|
|
17
|
+
allowOverrides = true
|
|
18
|
+
|
|
19
|
+
[commands]
|