Skip to content

Commit 2fe13b7

Browse files
committed
fixing legacy code
1 parent 507c792 commit 2fe13b7

5 files changed

Lines changed: 64 additions & 7 deletions

File tree

src/browser/chrome.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,18 @@ export function buildNativeHostManifest(options: {
389389
};
390390
}
391391

392+
function extensionIdFromAllowedOrigin(origin: string): string | null {
393+
const match = /^chrome-extension:\/\/([^/]+)\/$/.exec(origin);
394+
return match?.[1] ?? null;
395+
}
396+
397+
function mergeExtensionIds(extensionIds: string[], allowedOrigins: string[] | undefined): string[] {
398+
const existingIds = (allowedOrigins ?? [])
399+
.map(extensionIdFromAllowedOrigin)
400+
.filter((id): id is string => Boolean(id));
401+
return Array.from(new Set([...existingIds, ...extensionIds].filter(Boolean)));
402+
}
403+
392404
function resolveNodePath(): string {
393405
// Don't use bun or the compiled autohand binary as the shebang —
394406
// Chrome native messaging host scripts must use Node.js because they
@@ -623,13 +635,15 @@ export async function ensureNativeHostInstalled(options?: {
623635
const expectedExtensionIds = [options?.extensionId].filter((id): id is string => Boolean(id));
624636
const expectedAllowedOrigins = expectedExtensionIds.map((extensionId) => `chrome-extension://${extensionId}/`);
625637
const hostScriptPath = path.join(getBrowserDataRoot(homeDir), 'host.js');
638+
let installExtensionIds = expectedExtensionIds;
626639

627640
// If the Chrome manifest already exists and its host script is reachable
628641
// with a valid shebang and it is paired with the current extension id,
629642
// don't overwrite.
630643
if (await pathExists(chromeManifest.manifestPath)) {
631644
try {
632645
const manifest = await readJson(chromeManifest.manifestPath) as { path?: string; allowed_origins?: string[] };
646+
installExtensionIds = mergeExtensionIds(expectedExtensionIds, manifest.allowed_origins);
633647
if (manifest.path && await pathExists(manifest.path)) {
634648
// Check shebang is a valid Node.js interpreter (not bun, not the autohand binary itself)
635649
const firstLine = (await readFile(manifest.path, 'utf8')).split('\n')[0] ?? '';
@@ -656,7 +670,7 @@ export async function ensureNativeHostInstalled(options?: {
656670
const { command, args } = resolveCliLaunchSpec();
657671

658672
await installNativeHost({
659-
extensionIds: expectedExtensionIds,
673+
extensionIds: installExtensionIds,
660674
cliCommand: command,
661675
cliArgPrefix: args.length ? args : undefined,
662676
});

src/core/agent/AgentUIRuntime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export function initializeAgentUIManager(host: AgentUIRuntimeHost): void {
5151
// Ctrl+C handling - could trigger graceful shutdown
5252
},
5353
enableQueueInput: true,
54+
onImageDetected: (data: Buffer, mimeType: string, filename?: string) =>
55+
host.imageManager.add(data, mimeType, filename),
5456
filesProvider: () => host.workspaceFileCollector.getCachedFiles(),
5557
slashCommands: SLASH_COMMANDS,
5658
skillsProvider: () =>

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ async function runCLI(options: CLIOptions): Promise<void> {
11471147

11481148
// Handle --chrome flag: trigger Chrome handoff before entering interactive mode
11491149
if (options.chrome) {
1150-
// Ensure native host is installed and paired to the current extension id.
1150+
// Ensure native host is installed and paired to the configured extension id.
11511151
const { ensureNativeHostInstalled, createBrowserHandoff, buildChromeOpenUrl, openChromeContinuation } = await import('./browser/chrome.js');
11521152
const extensionId = config.chrome?.extensionId;
11531153
await ensureNativeHostInstalled({ extensionId }).catch(() => {});

tests/browser/chrome.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ describe('browser/chrome', () => {
449449

450450
// Regression: ensureNativeHostInstalled must repair stale manifests even
451451
// when the referenced host file is reachable. A valid shebang is not enough:
452-
// Chrome will reject the host if allowed_origins is still paired to an old
453-
// extension id such as ext123.
452+
// Chrome will reject the host if allowed_origins is paired to another
453+
// extension id.
454454
it('repairs manifest when the allowed origin does not match the extension id', async () => {
455455
const { getManifestTarget } = await import('../../src/browser/chrome.js');
456456
const target = getManifestTarget('chrome');
@@ -475,17 +475,20 @@ describe('browser/chrome', () => {
475475
description: 'test',
476476
path: hostPath,
477477
type: 'stdio',
478-
allowed_origins: ['chrome-extension://ext123/'],
478+
allowed_origins: ['chrome-extension://oldextensionid/'],
479479
});
480480

481481
// Re-import to get fresh module
482482
const { ensureNativeHostInstalled } = await import('../../src/browser/chrome.js');
483483

484-
await ensureNativeHostInstalled({ extensionId: 'testid' });
484+
await ensureNativeHostInstalled({ extensionId: 'newextensionid' });
485485

486486
const manifest = await readJson(target.manifestPath);
487487
expect(manifest.path).not.toBe(hostPath);
488-
expect(manifest.allowed_origins).toEqual(['chrome-extension://testid/']);
488+
expect(manifest.allowed_origins).toEqual([
489+
'chrome-extension://oldextensionid/',
490+
'chrome-extension://newextensionid/',
491+
]);
489492
expect(await pathExists(manifest.path)).toBe(true);
490493
} finally {
491494
// Restore original manifest

tests/core/agent.startup-ui.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,44 @@ describe('agent startup and active input UI', () => {
16891689
}
16901690
});
16911691

1692+
it('wires the image manager into Ink composer image detection', () => {
1693+
const agent = Object.create(AutohandAgent.prototype) as any;
1694+
let restoreStdoutTTY: () => void = () => {};
1695+
let restoreStdinTTY: () => void = () => {};
1696+
const imageData = Buffer.from('fake-png-data');
1697+
1698+
agent.useInkRenderer = true;
1699+
agent.ui = null;
1700+
agent.workspaceFileCollector = {
1701+
getCachedFiles: vi.fn(() => []),
1702+
};
1703+
agent.skillsRegistry = {
1704+
listSkills: vi.fn(() => []),
1705+
};
1706+
agent.imageManager = {
1707+
add: vi.fn(() => 42),
1708+
};
1709+
1710+
try {
1711+
restoreStdoutTTY = overrideStreamTTY(process.stdout, true);
1712+
restoreStdinTTY = overrideStreamTTY(process.stdin, true);
1713+
1714+
(agent as any).initializeUIManager();
1715+
1716+
const options = (agent.ui as any).options;
1717+
expect(options.onImageDetected).toBeTypeOf('function');
1718+
expect(options.onImageDetected(imageData, 'image/png', 'Screenshot.png')).toBe(42);
1719+
expect(agent.imageManager.add).toHaveBeenCalledWith(
1720+
imageData,
1721+
'image/png',
1722+
'Screenshot.png'
1723+
);
1724+
} finally {
1725+
restoreStdoutTTY();
1726+
restoreStdinTTY();
1727+
}
1728+
});
1729+
16921730
it('handleInkSubmittedInstruction executes shell commands immediately instead of queueing them', async () => {
16931731
const agent = Object.create(AutohandAgent.prototype) as any;
16941732
agent.inkRenderer = {

0 commit comments

Comments
 (0)