feat: expose browser receiver pairing API
This commit is contained in:
parent
e2d07145d6
commit
2b3c6d0557
|
|
@ -55,6 +55,10 @@ export interface SyncNowResult {
|
||||||
conflicts?: SyncConflict[];
|
conflicts?: SyncConflict[];
|
||||||
applyErrors?: string[];
|
applyErrors?: string[];
|
||||||
}
|
}
|
||||||
|
export interface BrowserReceiverPairing {
|
||||||
|
receiverUrl: string;
|
||||||
|
receiverToken: string;
|
||||||
|
}
|
||||||
export interface VerstakPluginAPI {
|
export interface VerstakPluginAPI {
|
||||||
readonly pluginId: string;
|
readonly pluginId: string;
|
||||||
settings: {
|
settings: {
|
||||||
|
|
@ -128,6 +132,10 @@ export interface VerstakPluginAPI {
|
||||||
resetKey(): Promise<void>;
|
resetKey(): Promise<void>;
|
||||||
now(): Promise<SyncNowResult>;
|
now(): Promise<SyncNowResult>;
|
||||||
};
|
};
|
||||||
|
browserReceiver: {
|
||||||
|
pairing(): Promise<BrowserReceiverPairing>;
|
||||||
|
rotateToken(): Promise<BrowserReceiverPairing>;
|
||||||
|
};
|
||||||
dispose?: () => void;
|
dispose?: () => void;
|
||||||
}
|
}
|
||||||
export declare function createPluginAPI(_pluginId: string): VerstakPluginAPI;
|
export declare function createPluginAPI(_pluginId: string): VerstakPluginAPI;
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gDAAgD;AA2KhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}
|
{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gDAAgD;AAqLhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQ3E,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAgB,EAAE,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CA2VlH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"}
|
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQ3E,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAgB,EAAE,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CAqWlH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"}
|
||||||
|
|
@ -412,6 +412,16 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options = {}) {
|
||||||
resetKey: vi.fn(async () => { }),
|
resetKey: vi.fn(async () => { }),
|
||||||
now: vi.fn(async () => ({ pushed: 0, pulled: 0, serverSequence: 0 })),
|
now: vi.fn(async () => ({ pushed: 0, pulled: 0, serverSequence: 0 })),
|
||||||
},
|
},
|
||||||
|
browserReceiver: {
|
||||||
|
pairing: vi.fn(async () => ({
|
||||||
|
receiverUrl: 'http://127.0.0.1:47731/api/browser-inbox/v1/captures',
|
||||||
|
receiverToken: 'mock-browser-receiver-token',
|
||||||
|
})),
|
||||||
|
rotateToken: vi.fn(async () => ({
|
||||||
|
receiverUrl: 'http://127.0.0.1:47731/api/browser-inbox/v1/captures',
|
||||||
|
receiverToken: 'mock-browser-receiver-token-rotated',
|
||||||
|
})),
|
||||||
|
},
|
||||||
dispose: vi.fn(),
|
dispose: vi.fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -45,7 +45,7 @@ export interface CapabilityEntry {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
status: 'stable' | 'draft' | 'deprecated';
|
status: 'stable' | 'draft' | 'deprecated';
|
||||||
}
|
}
|
||||||
export type Permission = 'vault.read' | 'vault.write' | 'vault.watch' | 'files.read' | 'files.write' | 'files.delete' | 'files.openExternal' | 'workbench.open' | 'storage.namespace' | 'storage.migrations' | 'events.publish' | 'events.subscribe' | 'ui.register' | 'commands.register' | 'network.local' | 'network.remote' | 'process.spawn' | 'secrets.read' | 'secrets.write' | 'sync.participate';
|
export type Permission = 'vault.read' | 'vault.write' | 'vault.watch' | 'files.read' | 'files.write' | 'files.delete' | 'files.openExternal' | 'workbench.open' | 'storage.namespace' | 'storage.migrations' | 'events.publish' | 'events.subscribe' | 'ui.register' | 'commands.register' | 'network.local' | 'network.remote' | 'process.spawn' | 'secrets.read' | 'secrets.write' | 'sync.participate' | 'browser.receiver.manage';
|
||||||
export interface PermissionEntry {
|
export interface PermissionEntry {
|
||||||
name: Permission;
|
name: Permission;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -100,7 +100,8 @@
|
||||||
"process.spawn",
|
"process.spawn",
|
||||||
"secrets.read",
|
"secrets.read",
|
||||||
"secrets.write",
|
"secrets.write",
|
||||||
"sync.participate"
|
"sync.participate",
|
||||||
|
"browser.receiver.manage"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"minItems": 1
|
"minItems": 1
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
{ "name": "process.spawn", "description": "Spawn external processes", "dangerous": true },
|
{ "name": "process.spawn", "description": "Spawn external processes", "dangerous": true },
|
||||||
{ "name": "secrets.read", "description": "Read secrets from the secret store", "dangerous": true },
|
{ "name": "secrets.read", "description": "Read secrets from the secret store", "dangerous": true },
|
||||||
{ "name": "secrets.write", "description": "Write secrets to the secret store", "dangerous": true },
|
{ "name": "secrets.write", "description": "Write secrets to the secret store", "dangerous": true },
|
||||||
{ "name": "sync.participate", "description": "Participate in vault sync", "dangerous": true }
|
{ "name": "sync.participate", "description": "Participate in vault sync", "dangerous": true },
|
||||||
|
{ "name": "browser.receiver.manage", "description": "View and rotate the local browser receiver pairing token", "dangerous": true }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ describe('VerstakPluginAPI contract', () => {
|
||||||
expect(typeof api.sync.configure).toBe('function');
|
expect(typeof api.sync.configure).toBe('function');
|
||||||
expect(typeof api.sync.resetKey).toBe('function');
|
expect(typeof api.sync.resetKey).toBe('function');
|
||||||
expect(typeof api.sync.now).toBe('function');
|
expect(typeof api.sync.now).toBe('function');
|
||||||
|
expect(typeof api.browserReceiver.pairing).toBe('function');
|
||||||
|
expect(typeof api.browserReceiver.rotateToken).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('manifest schema accepts files permissions used by platform-test', () => {
|
test('manifest schema accepts files permissions used by platform-test', () => {
|
||||||
|
|
@ -68,6 +70,14 @@ describe('VerstakPluginAPI contract', () => {
|
||||||
expect(permissionEnum).toEqual(expect.arrayContaining(['secrets.read', 'secrets.write']));
|
expect(permissionEnum).toEqual(expect.arrayContaining(['secrets.read', 'secrets.write']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('browser receiver token management is declared as a dangerous platform contract', () => {
|
||||||
|
const permissions = ((permissionsSchema as any).permissions || []) as Array<{ name: string; dangerous: boolean }>;
|
||||||
|
const permissionEnum = ((manifestSchema as any).properties.permissions.items.enum || []) as string[];
|
||||||
|
|
||||||
|
expect(permissions).toContainEqual(expect.objectContaining({ name: 'browser.receiver.manage', dangerous: true }));
|
||||||
|
expect(permissionEnum).toContain('browser.receiver.manage');
|
||||||
|
});
|
||||||
|
|
||||||
test('file.changed schema documents watcher refresh payload', () => {
|
test('file.changed schema documents watcher refresh payload', () => {
|
||||||
const fileChanged = (vaultEventsSchema as any).events.find((event: any) => event.name === 'file.changed');
|
const fileChanged = (vaultEventsSchema as any).events.find((event: any) => event.name === 'file.changed');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,11 @@ export interface SyncNowResult {
|
||||||
applyErrors?: string[];
|
applyErrors?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BrowserReceiverPairing {
|
||||||
|
receiverUrl: string;
|
||||||
|
receiverToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VerstakPluginAPI {
|
export interface VerstakPluginAPI {
|
||||||
readonly pluginId: string;
|
readonly pluginId: string;
|
||||||
|
|
||||||
|
|
@ -171,6 +176,11 @@ export interface VerstakPluginAPI {
|
||||||
now(): Promise<SyncNowResult>;
|
now(): Promise<SyncNowResult>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
browserReceiver: {
|
||||||
|
pairing(): Promise<BrowserReceiverPairing>;
|
||||||
|
rotateToken(): Promise<BrowserReceiverPairing>;
|
||||||
|
};
|
||||||
|
|
||||||
dispose?: () => void;
|
dispose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,16 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options: MockPlugi
|
||||||
resetKey: vi.fn(async () => {}),
|
resetKey: vi.fn(async () => {}),
|
||||||
now: vi.fn(async () => ({ pushed: 0, pulled: 0, serverSequence: 0 })),
|
now: vi.fn(async () => ({ pushed: 0, pulled: 0, serverSequence: 0 })),
|
||||||
},
|
},
|
||||||
|
browserReceiver: {
|
||||||
|
pairing: vi.fn(async () => ({
|
||||||
|
receiverUrl: 'http://127.0.0.1:47731/api/browser-inbox/v1/captures',
|
||||||
|
receiverToken: 'mock-browser-receiver-token',
|
||||||
|
})),
|
||||||
|
rotateToken: vi.fn(async () => ({
|
||||||
|
receiverUrl: 'http://127.0.0.1:47731/api/browser-inbox/v1/captures',
|
||||||
|
receiverToken: 'mock-browser-receiver-token-rotated',
|
||||||
|
})),
|
||||||
|
},
|
||||||
dispose: vi.fn(),
|
dispose: vi.fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,8 @@ export type Permission =
|
||||||
| 'process.spawn'
|
| 'process.spawn'
|
||||||
| 'secrets.read'
|
| 'secrets.read'
|
||||||
| 'secrets.write'
|
| 'secrets.write'
|
||||||
| 'sync.participate';
|
| 'sync.participate'
|
||||||
|
| 'browser.receiver.manage';
|
||||||
|
|
||||||
export interface PermissionEntry {
|
export interface PermissionEntry {
|
||||||
name: Permission;
|
name: Permission;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue