@openagents-org/agent-launcher 0.2.184 → 0.2.186
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/package.json +1 -1
- package/src/adapters/aider.js +2 -9
- package/src/adapters/amp.js +3 -15
- package/src/adapters/antigravity.js +3 -20
- package/src/adapters/base.js +307 -19
- package/src/adapters/claude.js +63 -47
- package/src/adapters/cline.js +2 -13
- package/src/adapters/codebuddy.js +3 -29
- package/src/adapters/codex.js +3 -13
- package/src/adapters/commandcode.js +3 -29
- package/src/adapters/copilot.js +15 -31
- package/src/adapters/cursor.js +8 -13
- package/src/adapters/deepseek.js +33 -68
- package/src/adapters/gemini.js +3 -20
- package/src/adapters/goose.js +4 -14
- package/src/adapters/hermes.js +2 -10
- package/src/adapters/kimi.js +2 -18
- package/src/adapters/llm-direct.js +17 -6
- package/src/adapters/mini.js +3 -14
- package/src/adapters/nanoclaw.js +25 -13
- package/src/adapters/openclaw.js +41 -0
- package/src/adapters/opencode.js +62 -40
- package/src/adapters/openworker.js +21 -22
- package/src/adapters/pi.js +22 -29
- package/src/daemon.js +54 -4
- package/src/wsl.js +60 -8
package/src/adapters/pi.js
CHANGED
|
@@ -147,9 +147,6 @@ class PiAdapter extends BaseAdapter {
|
|
|
147
147
|
// channel → child process (BaseAdapter/stop paths read this)
|
|
148
148
|
this._channelProcesses = {};
|
|
149
149
|
this._stoppingChannels = new Set();
|
|
150
|
-
// Dedup for "Execution stopped by user." — the control handler and the
|
|
151
|
-
// in-flight message handler both race to announce it (see claude.js).
|
|
152
|
-
this._stopNoticeSent = new Set();
|
|
153
150
|
|
|
154
151
|
this._sessionsFile = path.join(
|
|
155
152
|
os.homedir(), '.openagents', 'sessions',
|
|
@@ -524,24 +521,6 @@ class PiAdapter extends BaseAdapter {
|
|
|
524
521
|
// ------------------------------------------------------------------
|
|
525
522
|
|
|
526
523
|
async _onControlAction(action, payload) {
|
|
527
|
-
if (action === 'stop') {
|
|
528
|
-
const channel = (payload && typeof payload === 'object') ? payload.channel : null;
|
|
529
|
-
if (channel) {
|
|
530
|
-
const pp = this._persistentProcs[channel];
|
|
531
|
-
if (pp) pp.userStopped = true;
|
|
532
|
-
if (this._channelProcesses[channel]) {
|
|
533
|
-
this._log(`Stopping Pi for channel=${channel}`);
|
|
534
|
-
this._stoppingChannels.add(channel);
|
|
535
|
-
await this._abortChannel(channel);
|
|
536
|
-
delete this._channelQueues[channel];
|
|
537
|
-
await this._postStopNotice(channel);
|
|
538
|
-
}
|
|
539
|
-
} else {
|
|
540
|
-
for (const pp of Object.values(this._persistentProcs)) pp.userStopped = true;
|
|
541
|
-
await this._stopAllProcesses('Execution stopped by user.');
|
|
542
|
-
}
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
524
|
if (action === 'restart') {
|
|
546
525
|
const channel = (payload && typeof payload === 'object') ? payload.channel : null;
|
|
547
526
|
if (channel) {
|
|
@@ -594,11 +573,20 @@ class PiAdapter extends BaseAdapter {
|
|
|
594
573
|
}
|
|
595
574
|
}
|
|
596
575
|
|
|
597
|
-
/**
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
576
|
+
/**
|
|
577
|
+
* Stop ONE channel's Pi turn (BaseAdapter._handleUserStop drives this).
|
|
578
|
+
* `userStopped` goes on first so the turn that is winding down reports a
|
|
579
|
+
* stop rather than a failure.
|
|
580
|
+
*/
|
|
581
|
+
async _stopChannelWork(channel) {
|
|
582
|
+
const pp = this._persistentProcs[channel];
|
|
583
|
+
if (pp) pp.userStopped = true;
|
|
584
|
+
// Abort goes over the RPC channel of the persistent record; a bare child
|
|
585
|
+
// with no record is simply killed.
|
|
586
|
+
if (!pp || !this._channelProcesses[channel]) return super._stopChannelWork(channel);
|
|
587
|
+
this._log(`Stopping Pi for channel=${channel}`);
|
|
588
|
+
await this._abortChannel(channel);
|
|
589
|
+
return 'stopped';
|
|
602
590
|
}
|
|
603
591
|
|
|
604
592
|
/**
|
|
@@ -1346,7 +1334,6 @@ class PiAdapter extends BaseAdapter {
|
|
|
1346
1334
|
const channel = msg.sessionId || this.channelName || 'general';
|
|
1347
1335
|
|
|
1348
1336
|
this._stoppingChannels.delete(channel);
|
|
1349
|
-
this._stopNoticeSent.delete(channel);
|
|
1350
1337
|
|
|
1351
1338
|
if (!content && !attachments.length) return;
|
|
1352
1339
|
|
|
@@ -1400,6 +1387,9 @@ class PiAdapter extends BaseAdapter {
|
|
|
1400
1387
|
browserEnabled,
|
|
1401
1388
|
});
|
|
1402
1389
|
|
|
1390
|
+
// Stopped while this turn was being prepared: start nothing (no tokens).
|
|
1391
|
+
if (this._stoppedBeforeStart(channel)) return;
|
|
1392
|
+
|
|
1403
1393
|
let pp;
|
|
1404
1394
|
try {
|
|
1405
1395
|
pp = await this._ensureProc(channel, workingDir, systemPrompt);
|
|
@@ -1408,6 +1398,9 @@ class PiAdapter extends BaseAdapter {
|
|
|
1408
1398
|
return;
|
|
1409
1399
|
}
|
|
1410
1400
|
|
|
1401
|
+
// Starting the process takes a moment; the prompt is what costs tokens.
|
|
1402
|
+
if (this._stoppedBeforeStart(channel)) return;
|
|
1403
|
+
|
|
1411
1404
|
// Fresh per-turn accounting.
|
|
1412
1405
|
pp.msgChannel = channel;
|
|
1413
1406
|
pp.turnTexts = [];
|
|
@@ -1453,7 +1446,7 @@ class PiAdapter extends BaseAdapter {
|
|
|
1453
1446
|
pp.turnInFlight = false;
|
|
1454
1447
|
|
|
1455
1448
|
if (pp.userStopped || this._stoppingChannels.has(channel)) {
|
|
1456
|
-
|
|
1449
|
+
await this._finishUserStop(channel);
|
|
1457
1450
|
pp.userStopped = false;
|
|
1458
1451
|
return;
|
|
1459
1452
|
}
|
|
@@ -1470,7 +1463,7 @@ class PiAdapter extends BaseAdapter {
|
|
|
1470
1463
|
async _reportTurnFailure(pp, channel, error) {
|
|
1471
1464
|
const raw = (error && error.message) || String(error);
|
|
1472
1465
|
if (error instanceof PiCancelledError && (pp.userStopped || this._stoppingChannels.has(channel))) {
|
|
1473
|
-
await this.
|
|
1466
|
+
await this._finishUserStop(channel);
|
|
1474
1467
|
return;
|
|
1475
1468
|
}
|
|
1476
1469
|
const { kind, userMessage } = classifyPiError(raw);
|
package/src/daemon.js
CHANGED
|
@@ -23,6 +23,27 @@ function maskApiKey(key) {
|
|
|
23
23
|
return key.slice(0, 4) + '...' + key.slice(-4);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Top-level filesystem locations the workspace folder picker can jump to.
|
|
28
|
+
* Windows has one root per mounted drive; POSIX systems have a single root.
|
|
29
|
+
* Keeping this as a small pure helper makes the Windows behaviour testable on
|
|
30
|
+
* every CI platform.
|
|
31
|
+
*/
|
|
32
|
+
function filesystemRoots(platform = os.platform(), exists = fs.existsSync) {
|
|
33
|
+
if (platform !== 'win32') return ['/'];
|
|
34
|
+
|
|
35
|
+
const roots = [];
|
|
36
|
+
for (let code = 'A'.charCodeAt(0); code <= 'Z'.charCodeAt(0); code++) {
|
|
37
|
+
const root = `${String.fromCharCode(code)}:\\`;
|
|
38
|
+
try {
|
|
39
|
+
if (exists(root)) roots.push(root);
|
|
40
|
+
} catch {
|
|
41
|
+
// A disconnected or unreadable mapped drive should not break heartbeat.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return roots;
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
/**
|
|
27
48
|
* Agent process lifecycle manager.
|
|
28
49
|
*
|
|
@@ -327,8 +348,9 @@ class Daemon {
|
|
|
327
348
|
}
|
|
328
349
|
|
|
329
350
|
/**
|
|
330
|
-
* Filesystem hint for the working-directory picker: home
|
|
331
|
-
* (non-hidden) subfolders,
|
|
351
|
+
* Filesystem hint for the working-directory picker: home, its immediate
|
|
352
|
+
* (non-hidden) subfolders, and the device's filesystem roots. Roots let a
|
|
353
|
+
* Windows user switch drives instead of being trapped on the home drive.
|
|
332
354
|
*/
|
|
333
355
|
_buildFs() {
|
|
334
356
|
try {
|
|
@@ -338,7 +360,7 @@ class Daemon {
|
|
|
338
360
|
.map((d) => d.name)
|
|
339
361
|
.sort((a, b) => a.localeCompare(b))
|
|
340
362
|
.slice(0, 100);
|
|
341
|
-
return { home, dirs };
|
|
363
|
+
return { home, dirs, roots: filesystemRoots() };
|
|
342
364
|
} catch {
|
|
343
365
|
return {};
|
|
344
366
|
}
|
|
@@ -374,6 +396,31 @@ class Daemon {
|
|
|
374
396
|
if (nativeVar) await this._runAgn(['env', type, '--set', `${nativeVar}=${val}`]);
|
|
375
397
|
}
|
|
376
398
|
|
|
399
|
+
/**
|
|
400
|
+
* A node command targets one agent. The launcher may already have saved an
|
|
401
|
+
* instance-level model/key/URL, which wins over the type env written above.
|
|
402
|
+
* Mirror the command onto that instance so the running adapter and the
|
|
403
|
+
* launcher's "This Computer" row both see the requested settings.
|
|
404
|
+
*/
|
|
405
|
+
_saveNodeAgentEnv(name, type, args) {
|
|
406
|
+
const changes = {};
|
|
407
|
+
if (args.apiKey && !args.useDeviceCredentials) changes.LLM_API_KEY = args.apiKey;
|
|
408
|
+
if (args.baseUrl !== undefined) changes.LLM_BASE_URL = args.baseUrl;
|
|
409
|
+
if (args.model !== undefined) {
|
|
410
|
+
const existing = this.config.getAgent(name);
|
|
411
|
+
if (!existing) throw new Error(`Agent '${name}' not found after create`);
|
|
412
|
+
// Old instance-level native model keys can outrank LLM_MODEL in the
|
|
413
|
+
// launcher's display and in CLIs without a resolve_env model rule.
|
|
414
|
+
for (const key of Object.keys(existing.env || {})) {
|
|
415
|
+
if (key === 'MODEL' || key.endsWith('_MODEL')) changes[key] = '';
|
|
416
|
+
}
|
|
417
|
+
changes.LLM_MODEL = String(args.model || '');
|
|
418
|
+
const nativeVar = { claude: 'ANTHROPIC_MODEL', gemini: 'GEMINI_MODEL' }[type] || this._nativeModelVar(type);
|
|
419
|
+
if (nativeVar) changes[nativeVar] = String(args.model || '');
|
|
420
|
+
}
|
|
421
|
+
if (Object.keys(changes).length) this.config.updateAgentEnv(name, changes);
|
|
422
|
+
}
|
|
423
|
+
|
|
377
424
|
/**
|
|
378
425
|
* The agent's own model setting, for a type whose registry declares one and
|
|
379
426
|
* does not map LLM_MODEL onto anything itself (CodeArts reads CODEARTS_MODEL
|
|
@@ -477,6 +524,7 @@ async _runNodeCommand(n, cmd) {
|
|
|
477
524
|
if (args.model) await this._setModelEnv(type, args.model);
|
|
478
525
|
if (args.baseUrl) await this._runAgn(['env', type, '--set', `LLM_BASE_URL=${args.baseUrl}`]);
|
|
479
526
|
await this._applyConfigMap(type, args.config);
|
|
527
|
+
this._saveNodeAgentEnv(name, type, args);
|
|
480
528
|
// Attach to this node's workspace BY SLUG: the daemon already knows
|
|
481
529
|
// which workspace this command came from, so there is no token to
|
|
482
530
|
// pass and no /v1/token/resolve round-trip to fail (the outage class
|
|
@@ -519,6 +567,7 @@ async _runNodeCommand(n, cmd) {
|
|
|
519
567
|
await this._runAgn(['remove', name]);
|
|
520
568
|
const rc = await this._runAgn(['create', name, '--type', type, '--install', '--path', newDir]);
|
|
521
569
|
if (rc.code !== 0) throw new Error(rc.stderr || rc.stdout || 'recreate failed');
|
|
570
|
+
this._saveNodeAgentEnv(name, type, args);
|
|
522
571
|
const rc2 = await this._runAgn([
|
|
523
572
|
'connect', name, '--workspace', n.workspace_slug || n.workspace_id,
|
|
524
573
|
]);
|
|
@@ -528,6 +577,7 @@ async _runNodeCommand(n, cmd) {
|
|
|
528
577
|
// through daemon.cmd, and writeCommand() OVERWRITES that file — the
|
|
529
578
|
// start could clobber the unread stop (agent keeps the stale env) or
|
|
530
579
|
// interleave with the poll so the agent stops and never restarts.
|
|
580
|
+
this._saveNodeAgentEnv(name, type, args);
|
|
531
581
|
await this.restartAgent(name);
|
|
532
582
|
}
|
|
533
583
|
ok = true;
|
|
@@ -1722,4 +1772,4 @@ async _runNodeCommand(n, cmd) {
|
|
|
1722
1772
|
}
|
|
1723
1773
|
}
|
|
1724
1774
|
|
|
1725
|
-
module.exports = { Daemon };
|
|
1775
|
+
module.exports = { Daemon, filesystemRoots };
|
package/src/wsl.js
CHANGED
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
* The mirror direction needs no marker: a path ending in .exe/.cmd/.bat while
|
|
28
28
|
* we are running inside a distro is unambiguously a Windows binary.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* the
|
|
30
|
+
* Background resolution checks whether the default distro is already running
|
|
31
|
+
* before probing its login PATH or UNC mount. A stopped distro is left stopped;
|
|
32
|
+
* after the user starts it, the next scan can detect its CLIs. One PATH probe
|
|
33
|
+
* (5 min TTL) then serves later lookups through the \\wsl.localhost UNC mount.
|
|
33
34
|
*
|
|
34
35
|
* Inside the desktop app's main process none of that may block: starting
|
|
35
36
|
* wsl.exe can mean booting the WSL VM, and the first touch of the UNC mount
|
|
@@ -53,6 +54,9 @@ const CACHE_TTL_MS = 30 * 1000;
|
|
|
53
54
|
* the VM (and, in the desktop app, the window) busy for nothing.
|
|
54
55
|
*/
|
|
55
56
|
const MACHINE_TTL_MS = 5 * 60 * 1000;
|
|
57
|
+
// A stopped distro must never be woken by background detection. Keep this
|
|
58
|
+
// short so a distro the user starts becomes visible on the next scan.
|
|
59
|
+
const RUNNING_TTL_MS = 2000;
|
|
56
60
|
|
|
57
61
|
function _fresh(cache, ttl) {
|
|
58
62
|
return !!cache.at && Date.now() - cache.at < ttl;
|
|
@@ -126,6 +130,8 @@ function isRunningInWsl() {
|
|
|
126
130
|
|
|
127
131
|
let distrosCache = { value: null, at: 0 };
|
|
128
132
|
let distrosRefresh = null;
|
|
133
|
+
let runningDistrosCache = { value: null, at: 0 };
|
|
134
|
+
let runningDistrosRefresh = null;
|
|
129
135
|
|
|
130
136
|
function _parseDistros(buf) {
|
|
131
137
|
return Buffer.from(buf)
|
|
@@ -203,6 +209,49 @@ function defaultDistro() {
|
|
|
203
209
|
return wslDistros()[0] || null;
|
|
204
210
|
}
|
|
205
211
|
|
|
212
|
+
function _refreshRunningDistros() {
|
|
213
|
+
if (_fresh(runningDistrosCache, RUNNING_TTL_MS)) return Promise.resolve();
|
|
214
|
+
if (!runningDistrosRefresh) {
|
|
215
|
+
runningDistrosRefresh = _execFileOut('wsl.exe', ['-l', '--running', '-q'], {
|
|
216
|
+
encoding: 'buffer', maxBuffer: 1024 * 1024,
|
|
217
|
+
})
|
|
218
|
+
.then((out) => { runningDistrosCache = { value: out ? _parseDistros(out) : [], at: Date.now() }; })
|
|
219
|
+
.finally(() => { runningDistrosRefresh = null; });
|
|
220
|
+
}
|
|
221
|
+
return runningDistrosRefresh;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Query WSL's management state without executing a command inside a distro. */
|
|
225
|
+
function _defaultDistroRunning() {
|
|
226
|
+
const distro = defaultDistro();
|
|
227
|
+
if (!distro) return false;
|
|
228
|
+
if (!_fresh(runningDistrosCache, RUNNING_TTL_MS)) {
|
|
229
|
+
if (!canBlock()) {
|
|
230
|
+
void _refreshRunningDistros();
|
|
231
|
+
} else {
|
|
232
|
+
let names = [];
|
|
233
|
+
try {
|
|
234
|
+
names = _parseDistros(execFileSync('wsl.exe', ['-l', '--running', '-q'], {
|
|
235
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
236
|
+
windowsHide: true,
|
|
237
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
238
|
+
maxBuffer: 1024 * 1024,
|
|
239
|
+
}));
|
|
240
|
+
} catch { /* WSL unavailable: do not wake it */ }
|
|
241
|
+
runningDistrosCache = { value: names, at: Date.now() };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return (runningDistrosCache.value || []).includes(distro);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function _defaultDistroRunningAsync() {
|
|
248
|
+
await _refreshDistros();
|
|
249
|
+
const distro = (distrosCache.value || [])[0];
|
|
250
|
+
if (!distro) return false;
|
|
251
|
+
await _refreshRunningDistros();
|
|
252
|
+
return (runningDistrosCache.value || []).includes(distro);
|
|
253
|
+
}
|
|
254
|
+
|
|
206
255
|
// ---------------------------------------------------------------------------
|
|
207
256
|
// Windows side: reaching into the distro's filesystem without starting one
|
|
208
257
|
// ---------------------------------------------------------------------------
|
|
@@ -216,7 +265,7 @@ function _refreshUncRoot() {
|
|
|
216
265
|
if (_fresh(uncRootCache, MACHINE_TTL_MS)) return Promise.resolve();
|
|
217
266
|
if (!uncRootRefresh) {
|
|
218
267
|
uncRootRefresh = (async () => {
|
|
219
|
-
await
|
|
268
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
220
269
|
const distro = (distrosCache.value || [])[0];
|
|
221
270
|
if (!distro) return;
|
|
222
271
|
let root = null;
|
|
@@ -242,6 +291,7 @@ function _refreshUncRoot() {
|
|
|
242
291
|
* WSL2 serves \\wsl.localhost\<distro>; WSL1 and older builds only \\wsl$\.
|
|
243
292
|
*/
|
|
244
293
|
function _uncRoot() {
|
|
294
|
+
if (!_defaultDistroRunning()) return null;
|
|
245
295
|
if (_fresh(uncRootCache, MACHINE_TTL_MS)) {
|
|
246
296
|
return uncRootCache.value;
|
|
247
297
|
}
|
|
@@ -334,8 +384,7 @@ function _refreshShellProbe() {
|
|
|
334
384
|
if (_fresh(shellProbeCache, MACHINE_TTL_MS)) return Promise.resolve();
|
|
335
385
|
if (!shellProbeRefresh) {
|
|
336
386
|
shellProbeRefresh = (async () => {
|
|
337
|
-
await
|
|
338
|
-
if (!(distrosCache.value || []).length) return;
|
|
387
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
339
388
|
let out = null;
|
|
340
389
|
for (const argv of SHELL_PROBE_ARGVS) {
|
|
341
390
|
const raw = await _execFileOut('wsl.exe', argv, { encoding: 'utf-8', maxBuffer: 4 * 1024 * 1024 });
|
|
@@ -365,6 +414,7 @@ function _refreshShellProbe() {
|
|
|
365
414
|
*/
|
|
366
415
|
function _shellProbe() {
|
|
367
416
|
const empty = _emptyShellProbe();
|
|
417
|
+
if (!_defaultDistroRunning()) return empty;
|
|
368
418
|
if (_fresh(shellProbeCache, MACHINE_TTL_MS)) {
|
|
369
419
|
return shellProbeCache.value || empty;
|
|
370
420
|
}
|
|
@@ -428,7 +478,7 @@ const wslBinaryRefresh = new Map();
|
|
|
428
478
|
function _refreshWslBinary(key, list) {
|
|
429
479
|
if (wslBinaryRefresh.has(key)) return wslBinaryRefresh.get(key);
|
|
430
480
|
const job = (async () => {
|
|
431
|
-
await
|
|
481
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
432
482
|
let value = null;
|
|
433
483
|
if ((distrosCache.value || []).length) {
|
|
434
484
|
await Promise.all([_refreshUncRoot(), _refreshShellProbe()]);
|
|
@@ -465,13 +515,14 @@ function _refreshWslBinary(key, list) {
|
|
|
465
515
|
* Returns the path as the DISTRO sees it, behind the `wsl:` marker
|
|
466
516
|
* (`wsl:/home/u/.local/bin/claude`) — the inner path is what `wsl.exe -e` must
|
|
467
517
|
* be handed. Existence is proved through the UNC mount, so a machine with WSL
|
|
468
|
-
* installed but no distro pays
|
|
518
|
+
* installed but no running default distro pays only for WSL management queries.
|
|
469
519
|
*
|
|
470
520
|
* @param {string|string[]} names binary name(s), e.g. 'claude' or ['cursor-agent','agent']
|
|
471
521
|
* @returns {string|null} marked in-distro path
|
|
472
522
|
*/
|
|
473
523
|
function resolveWslBinary(names) {
|
|
474
524
|
if (!IS_WINDOWS) return null;
|
|
525
|
+
if (!_defaultDistroRunning()) return null;
|
|
475
526
|
const list = (Array.isArray(names) ? names : [names]).filter(Boolean);
|
|
476
527
|
if (!list.length) return null;
|
|
477
528
|
const key = list.join('\0');
|
|
@@ -520,6 +571,7 @@ function clearWslBinaryCache() {
|
|
|
520
571
|
/** Full reset, including the per-process probes. For tests and rare rescans. */
|
|
521
572
|
function clearWslCache() {
|
|
522
573
|
distrosCache = { value: null, at: 0 };
|
|
574
|
+
runningDistrosCache = { value: null, at: 0 };
|
|
523
575
|
uncRootCache = { value: null, at: 0 };
|
|
524
576
|
shellProbeCache = { value: null, at: 0 };
|
|
525
577
|
wslBinaryCache.clear();
|