pi-landstrip 0.17.13 → 0.17.14
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/README.md +8 -9
- package/config.ts +17 -9
- package/index.ts +158 -231
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ processes, but without the outer Landstrip OS sandbox. The extension warns once
|
|
|
54
54
|
per session. Agent tool permissions still apply, but they are not an OS isolation
|
|
55
55
|
boundary.
|
|
56
56
|
|
|
57
|
-
Trusted project config overrides global config. `/
|
|
57
|
+
Trusted project config overrides global config. `/sandbox` updates a trusted
|
|
58
58
|
project sandbox file when present, otherwise the global file. Pi versions
|
|
59
59
|
without a project-trust API use only global configuration.
|
|
60
60
|
|
|
@@ -84,13 +84,13 @@ task's dedicated session directory. These paths are required to construct a
|
|
|
84
84
|
normal Pi worker and are not persisted into `sandbox.json`. The worker receives
|
|
85
85
|
write access only to its own session and temporary directories.
|
|
86
86
|
|
|
87
|
-
Use `/
|
|
88
|
-
|
|
89
|
-
maximum number of concurrent subagents.
|
|
87
|
+
Use `/sandbox` inside Pi to inspect the active policy and toggle sandboxing. Use
|
|
88
|
+
`/agents` to select the primary role; its Settings tab controls the global and
|
|
89
|
+
trusted-project maximum number of concurrent subagents.
|
|
90
90
|
|
|
91
91
|
## Primary agents
|
|
92
92
|
|
|
93
|
-
The
|
|
93
|
+
The `/agents` selector provides OpenCode-compatible `build` and `plan` roles by default.
|
|
94
94
|
Build has normal development access; plan asks before shell commands and file
|
|
95
95
|
changes. The selection controls the root system prompt and permissions and is
|
|
96
96
|
restored with the session.
|
|
@@ -206,10 +206,9 @@ package entry. `subagents.json` accepts only top-level `maxSubagents` and
|
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
`maxSubagents` is an integer from 0 through 16 controlling concurrent worker
|
|
209
|
-
processes. Setting it to zero removes the `task` tool while retaining the
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
the Sandbox tab only reports effective policy and status.
|
|
209
|
+
processes. Setting it to zero removes the `task` tool while retaining the primary
|
|
210
|
+
roles. There is no separate subagent enable switch. The Settings tab in `/agents`
|
|
211
|
+
edits this limit separately for global and trusted-project configuration.
|
|
213
212
|
|
|
214
213
|
Agent modes, hidden/disabled agents, prompts, and ordered `allow`/`ask`/`deny`
|
|
215
214
|
permissions apply to primary agents and subagents. Subagent workers also honor
|
package/config.ts
CHANGED
|
@@ -76,21 +76,29 @@ export async function setMaxSubagentsConfig(
|
|
|
76
76
|
includeProject = true,
|
|
77
77
|
agentDir = getAgentDir(),
|
|
78
78
|
): Promise<'global' | 'project'> {
|
|
79
|
+
const { projectPath } = getLandstripConfigPaths(cwd, agentDir);
|
|
80
|
+
const scope = includeProject && existsSync(projectPath) ? 'project' : 'global';
|
|
81
|
+
await setMaxSubagentsConfigForScope(cwd, maxSubagents, scope, agentDir);
|
|
82
|
+
return scope;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function setMaxSubagentsConfigForScope(
|
|
86
|
+
cwd: string,
|
|
87
|
+
maxSubagents: number,
|
|
88
|
+
scope: 'global' | 'project',
|
|
89
|
+
agentDir = getAgentDir(),
|
|
90
|
+
): Promise<void> {
|
|
79
91
|
if (!Number.isInteger(maxSubagents) || maxSubagents < 0 || maxSubagents > MAX_SUBAGENTS) {
|
|
80
92
|
throw new Error(`maxSubagents must be an integer from 0 to ${MAX_SUBAGENTS}`);
|
|
81
93
|
}
|
|
82
94
|
const { globalPath, projectPath } = getLandstripConfigPaths(cwd, agentDir);
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
: { scope: 'global' as const, path: globalPath };
|
|
87
|
-
await withFileMutationQueue(target.path, async () => {
|
|
88
|
-
const config = readConfig(target.path);
|
|
95
|
+
const path = scope === 'project' ? projectPath : globalPath;
|
|
96
|
+
await withFileMutationQueue(path, async () => {
|
|
97
|
+
const config = readConfig(path);
|
|
89
98
|
config.maxSubagents = maxSubagents;
|
|
90
|
-
mkdirSync(dirname(
|
|
91
|
-
writeFileSync(
|
|
99
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
100
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
92
101
|
});
|
|
93
|
-
return target.scope;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
export function loadLandstripConfig(
|
package/index.ts
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
workerConfigFromEnvironment,
|
|
62
62
|
} from './subagents.ts';
|
|
63
63
|
import { availablePrimaryAgents } from './agents.ts';
|
|
64
|
-
import { MAX_SUBAGENTS,
|
|
64
|
+
import { loadLandstripConfig, MAX_SUBAGENTS, setMaxSubagentsConfigForScope } from './config.ts';
|
|
65
65
|
|
|
66
66
|
interface SandboxFilesystemConfig {
|
|
67
67
|
denyRead: string[];
|
|
@@ -814,10 +814,11 @@ function promptWriteBlock(
|
|
|
814
814
|
);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
-
// The broker knows only address:port, and no sandbox field can express a
|
|
818
|
-
//
|
|
819
|
-
//
|
|
820
|
-
// So a connection is granted for the session
|
|
817
|
+
// The broker knows only address:port, and no sandbox field can express a grant
|
|
818
|
+
// for one non-loopback endpoint: allowedDomains is enforced by the proxy,
|
|
819
|
+
// allowLocalBinding grants all loopback endpoints, and allowNetwork disables
|
|
820
|
+
// network enforcement. So a non-loopback connection is granted for the session
|
|
821
|
+
// or not at all.
|
|
821
822
|
function promptNetworkBlock(
|
|
822
823
|
ctx: ExtensionContext,
|
|
823
824
|
operation: string,
|
|
@@ -2359,63 +2360,98 @@ export function createLandstripIntegration(
|
|
|
2359
2360
|
|
|
2360
2361
|
enableSandbox(ctx);
|
|
2361
2362
|
});
|
|
2362
|
-
|
|
2363
|
-
description: 'Show
|
|
2363
|
+
maybePi.registerCommand?.('sandbox', {
|
|
2364
|
+
description: 'Show config and toggle the sandbox',
|
|
2364
2365
|
handler: async (_args, ctx) => {
|
|
2365
|
-
|
|
2366
|
-
|
|
2366
|
+
if (!ctx.hasUI) return;
|
|
2367
|
+
const config = loadConfig(ctx.cwd);
|
|
2367
2368
|
const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
|
|
2369
|
+
const status = noSandboxFlag
|
|
2370
|
+
? 'disabled by --no-sandbox'
|
|
2371
|
+
: config.enabled && sandboxEnabled && sandboxReady
|
|
2372
|
+
? 'active'
|
|
2373
|
+
: config.enabled
|
|
2374
|
+
? 'inactive'
|
|
2375
|
+
: 'disabled by config';
|
|
2376
|
+
const list = (values: string[]): string => values.join(', ') || 'none';
|
|
2377
|
+
const summary = [
|
|
2378
|
+
`Status: ${status}`,
|
|
2379
|
+
`landstrip package binary: ${landstripDisplayPath()}`,
|
|
2380
|
+
'',
|
|
2381
|
+
'Config files',
|
|
2382
|
+
`${projectPath} ${existsSync(projectPath) ? '(found)' : '(missing)'}`,
|
|
2383
|
+
`${globalPath} ${existsSync(globalPath) ? '(found)' : '(missing)'}`,
|
|
2384
|
+
'',
|
|
2385
|
+
`Network: ${config.network.allowNetwork ? 'unrestricted' : 'proxied'}`,
|
|
2386
|
+
`allow network: ${config.network.allowNetwork ? 'yes' : 'no'}`,
|
|
2387
|
+
`allowed: ${list(config.network.allowedDomains)}`,
|
|
2388
|
+
`denied: ${list(config.network.deniedDomains)}`,
|
|
2389
|
+
`unix sockets: ${config.network.allowAllUnixSockets ? 'all' : list(config.network.allowUnixSockets)}`,
|
|
2390
|
+
'',
|
|
2391
|
+
'Filesystem',
|
|
2392
|
+
`deny read: ${list(config.filesystem.denyRead)}`,
|
|
2393
|
+
`allow read: ${list(config.filesystem.allowRead)}`,
|
|
2394
|
+
`allow write: ${list(config.filesystem.allowWrite)}`,
|
|
2395
|
+
`deny write: ${list(config.filesystem.denyWrite)}`,
|
|
2396
|
+
].join('\n');
|
|
2397
|
+
const enabled = !config.enabled;
|
|
2398
|
+
const confirmed = await ctx.ui.confirm(
|
|
2399
|
+
'Sandbox',
|
|
2400
|
+
`${summary}\n\n${enabled ? 'Enable' : 'Disable'} the sandbox?`,
|
|
2401
|
+
);
|
|
2402
|
+
if (!confirmed) return;
|
|
2368
2403
|
|
|
2404
|
+
try {
|
|
2405
|
+
const scope = await setSandboxConfigEnabled(ctx.cwd, enabled, projectConfigTrusted);
|
|
2406
|
+
if (!enabled) {
|
|
2407
|
+
disableSandbox(ctx);
|
|
2408
|
+
} else if (!noSandboxFlag) {
|
|
2409
|
+
enableSandbox(ctx);
|
|
2410
|
+
}
|
|
2411
|
+
if (enabled && noSandboxFlag) {
|
|
2412
|
+
notify(ctx, 'Sandbox remains disabled via --no-sandbox', 'warning');
|
|
2413
|
+
} else {
|
|
2414
|
+
notify(ctx, `Sandbox ${enabled ? 'enabled' : 'disabled'} in ${scope} config`, 'info');
|
|
2415
|
+
}
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
notify(ctx, `Could not update config: ${error}`, 'error');
|
|
2418
|
+
}
|
|
2419
|
+
},
|
|
2420
|
+
});
|
|
2421
|
+
|
|
2422
|
+
maybePi.registerCommand?.('agents', {
|
|
2423
|
+
description: 'Select the primary agent and configure subagents',
|
|
2424
|
+
handler: async (_args, ctx) => {
|
|
2369
2425
|
if (!ctx.hasUI) return;
|
|
2370
2426
|
await ctx.ui.custom(
|
|
2371
2427
|
(tui, theme, _kb, done) => {
|
|
2372
|
-
let tab: '
|
|
2373
|
-
|
|
2428
|
+
let tab: 'agents' | 'settings' = 'agents';
|
|
2429
|
+
const initialAgents = runtime
|
|
2430
|
+
? availablePrimaryAgents(runtime.getAgentCatalog(ctx))
|
|
2431
|
+
: [];
|
|
2432
|
+
let selectedAgent = Math.max(
|
|
2433
|
+
0,
|
|
2434
|
+
initialAgents.findIndex((agent) => agent.name === runtime?.getPrimaryAgent()?.name),
|
|
2435
|
+
);
|
|
2374
2436
|
let selectedSetting = 0;
|
|
2375
|
-
let
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
const
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
if (!config.enabled) return { color: 'warning', label: 'Disabled' };
|
|
2385
|
-
if (!sandboxEnabled || !sandboxReady) return { color: 'warning', label: 'Inactive' };
|
|
2386
|
-
return { color: 'success', label: 'Active' };
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
|
-
function boolVal(v: boolean): string {
|
|
2390
|
-
return v ? theme.fg('warning', 'yes') : theme.fg('success', 'no');
|
|
2391
|
-
}
|
|
2437
|
+
let values = [
|
|
2438
|
+
loadLandstripConfig(ctx.cwd, false).maxSubagents,
|
|
2439
|
+
loadLandstripConfig(ctx.cwd, projectConfigTrusted).maxSubagents,
|
|
2440
|
+
];
|
|
2441
|
+
let editing = false;
|
|
2442
|
+
const dim = (value: string) => theme.fg('dim', value);
|
|
2443
|
+
const muted = (value: string) => theme.fg('muted', value);
|
|
2444
|
+
const accent = (value: string) => theme.fg('accent', value);
|
|
2445
|
+
const text = (value: string) => theme.fg('text', value);
|
|
2392
2446
|
|
|
2393
2447
|
return {
|
|
2394
2448
|
render(width: number): string[] {
|
|
2395
|
-
const innerW = Math.max(1, width - 4);
|
|
2396
2449
|
const row = (content = '') => boxRow(theme, width, content);
|
|
2397
|
-
const lines
|
|
2398
|
-
const status = sandboxStatus();
|
|
2399
|
-
function section(titleText: string, detail?: string): void {
|
|
2400
|
-
lines.push(row(''));
|
|
2401
|
-
lines.push(row(`${accent(titleText)}${detail ? dim(` · ${detail}`) : ''}`));
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
|
-
function item(label: string, value: string): void {
|
|
2405
|
-
lines.push(row(` ${dim('•')} ${muted(label.padEnd(13))} ${value}`));
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
function listValue(values: string[], maxWidth: number): string {
|
|
2409
|
-
const value = values.join(', ') || 'none';
|
|
2410
|
-
return text(truncateToWidth(value, Math.max(10, maxWidth)));
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
lines.push(boxTop(theme, width, 'Landstrip'));
|
|
2414
|
-
const sandboxTab = tab === 'sandbox' ? accent('Sandbox') : muted('Sandbox');
|
|
2415
|
-
const agentsTab = tab === 'agents' ? accent('Agents') : muted('Agents');
|
|
2416
|
-
const settingsTab = tab === 'settings' ? accent('Settings') : muted('Settings');
|
|
2450
|
+
const lines = [boxTop(theme, width, 'Agents')];
|
|
2417
2451
|
lines.push(
|
|
2418
|
-
row(
|
|
2452
|
+
row(
|
|
2453
|
+
` ${tab === 'agents' ? accent('Agents') : muted('Agents')} ${dim('│')} ${tab === 'settings' ? accent('Settings') : muted('Settings')}`,
|
|
2454
|
+
),
|
|
2419
2455
|
row(''),
|
|
2420
2456
|
);
|
|
2421
2457
|
|
|
@@ -2423,101 +2459,44 @@ export function createLandstripIntegration(
|
|
|
2423
2459
|
const catalog = runtime?.getAgentCatalog(ctx);
|
|
2424
2460
|
const agents = catalog ? availablePrimaryAgents(catalog) : [];
|
|
2425
2461
|
const activeAgent = runtime?.getPrimaryAgent()?.name;
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
const description = agent.description ? dim(` · ${agent.description}`) : '';
|
|
2436
|
-
lines.push(row(`${cursor} ${active} ${name}${description}`));
|
|
2437
|
-
}
|
|
2462
|
+
selectedAgent = Math.min(selectedAgent, Math.max(0, agents.length - 1));
|
|
2463
|
+
if (agents.length === 0) lines.push(row(muted('No primary agents configured')));
|
|
2464
|
+
for (const [index, agent] of agents.entries()) {
|
|
2465
|
+
const selected = index === selectedAgent;
|
|
2466
|
+
const cursor = selected ? accent('›') : ' ';
|
|
2467
|
+
const active = agent.name === activeAgent ? theme.fg('success', '●') : dim('○');
|
|
2468
|
+
const name = selected ? accent(agent.name) : text(agent.name);
|
|
2469
|
+
const description = agent.description ? dim(` ${agent.description}`) : '';
|
|
2470
|
+
lines.push(row(`${cursor} ${active} ${name}${description}`));
|
|
2438
2471
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
const checkbox = config.enabled ? theme.fg('success', '[x]') : muted('[ ]');
|
|
2453
|
-
const maxValue =
|
|
2454
|
-
selectedSetting === 1
|
|
2455
|
-
? accent(`[ ${maxSubagentsValue} ]`)
|
|
2456
|
-
: text(`[ ${maxSubagentsValue} ]`);
|
|
2457
|
-
lines.push(
|
|
2458
|
-
row(`${sandboxCursor} ${checkbox} ${text('Sandbox')}`),
|
|
2459
|
-
row(
|
|
2460
|
-
`${maxCursor} ${maxValue} ${text('Maximum concurrent subagents')} ${dim(`(0-${MAX_SUBAGENTS})`)}`,
|
|
2461
|
-
),
|
|
2462
|
-
);
|
|
2463
|
-
if (maxSubagentsValue === 0) {
|
|
2464
|
-
lines.push(row(` ${dim('Task delegation is disabled')}`));
|
|
2472
|
+
} else {
|
|
2473
|
+
const scopes = ['Global', 'Project'] as const;
|
|
2474
|
+
for (const [index, scope] of scopes.entries()) {
|
|
2475
|
+
const selected = index === selectedSetting;
|
|
2476
|
+
const unavailable = scope === 'Project' && !projectConfigTrusted;
|
|
2477
|
+
const cursor = selected ? accent('›') : ' ';
|
|
2478
|
+
const value = selected
|
|
2479
|
+
? accent(`[ ${values[index]} ]`)
|
|
2480
|
+
: text(`[ ${values[index]} ]`);
|
|
2481
|
+
const label = unavailable
|
|
2482
|
+
? muted(`${scope} (project not trusted)`)
|
|
2483
|
+
: text(scope);
|
|
2484
|
+
lines.push(row(`${cursor} ${value} ${label}`));
|
|
2465
2485
|
}
|
|
2466
|
-
if (
|
|
2467
|
-
lines.push(row(` ${dim('
|
|
2486
|
+
if (values[selectedSetting] === 0) {
|
|
2487
|
+
lines.push(row(` ${dim('Task delegation is disabled for this scope')}`));
|
|
2468
2488
|
}
|
|
2469
|
-
lines.push(
|
|
2470
|
-
row(''),
|
|
2471
|
-
row(
|
|
2472
|
-
`${dim('↑↓')} ${muted('select')} ${dim('space/enter')} ${muted('toggle or save')} ${dim('0-9')} ${muted('edit number')} ${dim('tab')} ${muted('switch')} ${dim('esc')} ${muted('close')}`,
|
|
2473
|
-
),
|
|
2474
|
-
boxBottom(theme, width),
|
|
2475
|
-
);
|
|
2476
|
-
return lines;
|
|
2477
2489
|
}
|
|
2478
2490
|
|
|
2479
|
-
const statusDot = theme.fg(status.color, '●');
|
|
2480
|
-
const pathSnippet = text(
|
|
2481
|
-
truncateToWidth(landstripDisplayPath(), Math.max(20, innerW - 28)),
|
|
2482
|
-
);
|
|
2483
2491
|
lines.push(
|
|
2492
|
+
row(''),
|
|
2484
2493
|
row(
|
|
2485
|
-
|
|
2494
|
+
tab === 'agents'
|
|
2495
|
+
? `${dim('↑↓')} ${muted('select')} ${dim('enter')} ${muted('activate')} ${dim('tab')} ${muted('settings')} ${dim('esc')} ${muted('close')}`
|
|
2496
|
+
: `${dim('↑↓')} ${muted('scope')} ${dim('0-9/+/-')} ${muted('change')} ${dim('enter')} ${muted('save')} ${dim('tab')} ${muted('agents')} ${dim('esc')} ${muted('close')}`,
|
|
2486
2497
|
),
|
|
2498
|
+
boxBottom(theme, width),
|
|
2487
2499
|
);
|
|
2488
|
-
|
|
2489
|
-
section('Config');
|
|
2490
|
-
item('project', text(projectPath));
|
|
2491
|
-
item('global', text(globalPath));
|
|
2492
|
-
|
|
2493
|
-
const netMode = config.network.allowNetwork ? 'unrestricted' : 'proxied';
|
|
2494
|
-
section('Network', netMode);
|
|
2495
|
-
item('allow network', boolVal(config.network.allowNetwork));
|
|
2496
|
-
item('allowed', listValue(config.network.allowedDomains, innerW - 17));
|
|
2497
|
-
item('denied', listValue(config.network.deniedDomains, innerW - 17));
|
|
2498
|
-
if (sessionAllowedDomains.length > 0)
|
|
2499
|
-
item('session', theme.fg('accent', sessionAllowedDomains.join(', ')));
|
|
2500
|
-
|
|
2501
|
-
section('Filesystem');
|
|
2502
|
-
item('deny read', listValue(config.filesystem.denyRead, innerW - 17));
|
|
2503
|
-
item('allow read', listValue(config.filesystem.allowRead, innerW - 17));
|
|
2504
|
-
item('allow write', listValue(config.filesystem.allowWrite, innerW - 17));
|
|
2505
|
-
item('deny write', listValue(config.filesystem.denyWrite, innerW - 17));
|
|
2506
|
-
|
|
2507
|
-
if (sessionAllowedReadPaths.length > 0 || sessionAllowedWritePaths.length > 0) {
|
|
2508
|
-
section('Session grants');
|
|
2509
|
-
if (sessionAllowedReadPaths.length > 0)
|
|
2510
|
-
item('read', theme.fg('accent', sessionAllowedReadPaths.join(', ')));
|
|
2511
|
-
if (sessionAllowedWritePaths.length > 0)
|
|
2512
|
-
item('write', theme.fg('accent', sessionAllowedWritePaths.join(', ')));
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
lines.push(row(''));
|
|
2516
|
-
lines.push(
|
|
2517
|
-
row(`${dim('tab')} ${muted('switch')} ${dim('esc')} ${muted('close')}`),
|
|
2518
|
-
);
|
|
2519
|
-
lines.push(boxBottom(theme, width));
|
|
2520
|
-
|
|
2521
2500
|
return lines;
|
|
2522
2501
|
},
|
|
2523
2502
|
|
|
@@ -2526,81 +2505,64 @@ export function createLandstripIntegration(
|
|
|
2526
2505
|
done(undefined);
|
|
2527
2506
|
return;
|
|
2528
2507
|
}
|
|
2529
|
-
if (
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
return;
|
|
2537
|
-
}
|
|
2538
|
-
if (matchesKey(data, 'left')) {
|
|
2539
|
-
tab = tab === 'settings' ? 'agents' : tab === 'agents' ? 'sandbox' : 'settings';
|
|
2540
|
-
if (tab === 'settings') {
|
|
2541
|
-
maxSubagentsValue = runtime?.getMaxSubagents() ?? 0;
|
|
2542
|
-
editingMaxSubagents = false;
|
|
2543
|
-
}
|
|
2508
|
+
if (
|
|
2509
|
+
matchesKey(data, 'tab') ||
|
|
2510
|
+
matchesKey(data, 'left') ||
|
|
2511
|
+
matchesKey(data, 'right')
|
|
2512
|
+
) {
|
|
2513
|
+
tab = tab === 'agents' ? 'settings' : 'agents';
|
|
2514
|
+
editing = false;
|
|
2544
2515
|
tui.requestRender();
|
|
2545
2516
|
return;
|
|
2546
2517
|
}
|
|
2547
2518
|
if (tab === 'agents') {
|
|
2548
2519
|
const catalog = runtime?.getAgentCatalog(ctx);
|
|
2549
2520
|
const agents = catalog ? availablePrimaryAgents(catalog) : [];
|
|
2550
|
-
if (matchesKey(data, 'up'))
|
|
2551
|
-
|
|
2552
|
-
tui.requestRender();
|
|
2553
|
-
} else if (matchesKey(data, 'down')) {
|
|
2521
|
+
if (matchesKey(data, 'up')) selectedAgent = Math.max(0, selectedAgent - 1);
|
|
2522
|
+
else if (matchesKey(data, 'down')) {
|
|
2554
2523
|
selectedAgent = Math.min(Math.max(0, agents.length - 1), selectedAgent + 1);
|
|
2555
|
-
tui.requestRender();
|
|
2556
2524
|
} else if (matchesKey(data, 'return')) {
|
|
2557
2525
|
const agent = agents[selectedAgent];
|
|
2558
|
-
if (agent
|
|
2559
|
-
}
|
|
2526
|
+
if (agent) runtime?.selectPrimaryAgent(agent.name, ctx);
|
|
2527
|
+
} else return;
|
|
2528
|
+
tui.requestRender();
|
|
2560
2529
|
return;
|
|
2561
2530
|
}
|
|
2562
2531
|
|
|
2563
|
-
if (tab !== 'settings') return;
|
|
2564
2532
|
if (matchesKey(data, 'up')) {
|
|
2565
2533
|
selectedSetting = Math.max(0, selectedSetting - 1);
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
return;
|
|
2569
|
-
}
|
|
2570
|
-
if (matchesKey(data, 'down')) {
|
|
2534
|
+
editing = false;
|
|
2535
|
+
} else if (matchesKey(data, 'down')) {
|
|
2571
2536
|
selectedSetting = Math.min(1, selectedSetting + 1);
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
if (/^[0-9]$/.test(data)) {
|
|
2579
|
-
const value = Number(
|
|
2580
|
-
editingMaxSubagents ? `${maxSubagentsValue}${data}` : data,
|
|
2581
|
-
);
|
|
2582
|
-
if (value <= MAX_SUBAGENTS) {
|
|
2583
|
-
maxSubagentsValue = value;
|
|
2584
|
-
editingMaxSubagents = true;
|
|
2585
|
-
tui.requestRender();
|
|
2586
|
-
}
|
|
2587
|
-
return;
|
|
2537
|
+
editing = false;
|
|
2538
|
+
} else if (/^[0-9]$/.test(data)) {
|
|
2539
|
+
const value = Number(editing ? `${values[selectedSetting]}${data}` : data);
|
|
2540
|
+
if (value <= MAX_SUBAGENTS) {
|
|
2541
|
+
values[selectedSetting] = value;
|
|
2542
|
+
editing = true;
|
|
2588
2543
|
}
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2544
|
+
} else if (data === '+' || data === '-') {
|
|
2545
|
+
values[selectedSetting] = Math.min(
|
|
2546
|
+
MAX_SUBAGENTS,
|
|
2547
|
+
Math.max(0, values[selectedSetting] + (data === '+' ? 1 : -1)),
|
|
2548
|
+
);
|
|
2549
|
+
editing = false;
|
|
2550
|
+
} else if (matchesKey(data, 'return')) {
|
|
2551
|
+
const scope = selectedSetting === 0 ? 'global' : 'project';
|
|
2552
|
+
if (scope === 'project' && !projectConfigTrusted) {
|
|
2553
|
+
notify(ctx, 'Project settings require a trusted project', 'warning');
|
|
2596
2554
|
return;
|
|
2597
2555
|
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2556
|
+
const maxSubagents = values[selectedSetting];
|
|
2557
|
+
void setMaxSubagentsConfigForScope(ctx.cwd, maxSubagents, scope)
|
|
2558
|
+
.then(() => {
|
|
2559
|
+
const effective = loadLandstripConfig(
|
|
2560
|
+
ctx.cwd,
|
|
2561
|
+
projectConfigTrusted,
|
|
2562
|
+
).maxSubagents;
|
|
2563
|
+
runtime?.setMaxSubagents(effective);
|
|
2564
|
+
values = [loadLandstripConfig(ctx.cwd, false).maxSubagents, effective];
|
|
2565
|
+
editing = false;
|
|
2604
2566
|
notify(
|
|
2605
2567
|
ctx,
|
|
2606
2568
|
`Maximum concurrent subagents set to ${maxSubagents} in ${scope} config`,
|
|
@@ -2612,34 +2574,8 @@ export function createLandstripIntegration(
|
|
|
2612
2574
|
notify(ctx, `Could not update config: ${error}`, 'error');
|
|
2613
2575
|
});
|
|
2614
2576
|
return;
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
if (data !== ' ' && !matchesKey(data, 'return')) return;
|
|
2618
|
-
|
|
2619
|
-
void (async () => {
|
|
2620
|
-
const enabled = !config.enabled;
|
|
2621
|
-
const scope = await setSandboxConfigEnabled(
|
|
2622
|
-
ctx.cwd,
|
|
2623
|
-
enabled,
|
|
2624
|
-
projectConfigTrusted,
|
|
2625
|
-
);
|
|
2626
|
-
config = loadConfig(ctx.cwd);
|
|
2627
|
-
|
|
2628
|
-
if (!enabled) {
|
|
2629
|
-
disableSandbox(ctx);
|
|
2630
|
-
notify(ctx, `Sandbox disabled in ${scope} config`, 'info');
|
|
2631
|
-
} else if (noSandboxFlag) {
|
|
2632
|
-
notify(ctx, 'Sandbox remains disabled via --no-sandbox', 'warning');
|
|
2633
|
-
} else if (!config.enabled) {
|
|
2634
|
-
notify(ctx, 'Sandbox remains disabled via config', 'info');
|
|
2635
|
-
} else if (enableSandbox(ctx)) {
|
|
2636
|
-
notify(ctx, `Sandbox enabled in ${scope} config`, 'info');
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
tui.requestRender();
|
|
2640
|
-
})().catch((error: unknown) => {
|
|
2641
|
-
notify(ctx, `Could not update config: ${error}`, 'error');
|
|
2642
|
-
});
|
|
2577
|
+
} else return;
|
|
2578
|
+
tui.requestRender();
|
|
2643
2579
|
},
|
|
2644
2580
|
|
|
2645
2581
|
invalidate(): void {},
|
|
@@ -2647,19 +2583,10 @@ export function createLandstripIntegration(
|
|
|
2647
2583
|
},
|
|
2648
2584
|
{
|
|
2649
2585
|
overlay: true,
|
|
2650
|
-
overlayOptions: {
|
|
2651
|
-
anchor: 'center',
|
|
2652
|
-
width: 78,
|
|
2653
|
-
margin: 2,
|
|
2654
|
-
},
|
|
2586
|
+
overlayOptions: { anchor: 'center', width: 72, margin: 2 },
|
|
2655
2587
|
},
|
|
2656
2588
|
);
|
|
2657
2589
|
},
|
|
2658
|
-
};
|
|
2659
|
-
maybePi.registerCommand?.('landstrip', landstripCommand);
|
|
2660
|
-
maybePi.registerCommand?.('sandbox', {
|
|
2661
|
-
...landstripCommand,
|
|
2662
|
-
description: 'Deprecated alias for /landstrip',
|
|
2663
2590
|
});
|
|
2664
2591
|
}
|
|
2665
2592
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.14",
|
|
4
4
|
"description": "Sandbox-aware Pi subagents and interactive command permissions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"landstrip",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@earendil-works/pi-tui": "^0.80.6",
|
|
41
|
-
"@landstrip/landstrip": "^0.17.
|
|
41
|
+
"@landstrip/landstrip": "^0.17.14",
|
|
42
42
|
"minimatch": "^10.2.5"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|