@nexface/tools 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.
Files changed (82) hide show
  1. package/dist/browser/controller.d.ts +30 -0
  2. package/dist/browser/controller.js +783 -0
  3. package/dist/browser/controller.js.map +1 -0
  4. package/dist/browser/dom.d.ts +43 -0
  5. package/dist/browser/dom.js +438 -0
  6. package/dist/browser/dom.js.map +1 -0
  7. package/dist/browser/feedback.d.ts +4 -0
  8. package/dist/browser/feedback.js +5 -0
  9. package/dist/browser/feedback.js.map +1 -0
  10. package/dist/browser/locator.d.ts +19 -0
  11. package/dist/browser/locator.js +277 -0
  12. package/dist/browser/locator.js.map +1 -0
  13. package/dist/browser/prompt.d.ts +5 -0
  14. package/dist/browser/prompt.generated.d.ts +1 -0
  15. package/dist/browser/prompt.generated.js +50 -0
  16. package/dist/browser/prompt.generated.js.map +1 -0
  17. package/dist/browser/prompt.js +9 -0
  18. package/dist/browser/prompt.js.map +1 -0
  19. package/dist/browser/snapshot.d.ts +7 -0
  20. package/dist/browser/snapshot.js +424 -0
  21. package/dist/browser/snapshot.js.map +1 -0
  22. package/dist/browser/toolset.d.ts +35 -0
  23. package/dist/browser/toolset.js +383 -0
  24. package/dist/browser/toolset.js.map +1 -0
  25. package/dist/browser/types.d.ts +129 -0
  26. package/dist/browser/types.js +2 -0
  27. package/dist/browser/types.js.map +1 -0
  28. package/dist/browser/worker-runner.d.ts +11 -0
  29. package/dist/browser/worker-runner.js +400 -0
  30. package/dist/browser/worker-runner.js.map +1 -0
  31. package/dist/definition.d.ts +16 -0
  32. package/dist/definition.js +72 -0
  33. package/dist/definition.js.map +1 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +4 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/internal-react.d.ts +12 -0
  38. package/dist/internal-react.js +23 -0
  39. package/dist/internal-react.js.map +1 -0
  40. package/dist/internal-types.d.ts +11 -0
  41. package/dist/internal-types.js +2 -0
  42. package/dist/internal-types.js.map +1 -0
  43. package/dist/internal.d.ts +9 -0
  44. package/dist/internal.js +5 -0
  45. package/dist/internal.js.map +1 -0
  46. package/dist/react.d.ts +25 -0
  47. package/dist/react.js +258 -0
  48. package/dist/react.js.map +1 -0
  49. package/dist/runtime.d.ts +93 -0
  50. package/dist/runtime.js +492 -0
  51. package/dist/runtime.js.map +1 -0
  52. package/dist/schema.d.ts +2 -0
  53. package/dist/schema.js +46 -0
  54. package/dist/schema.js.map +1 -0
  55. package/dist/types.d.ts +80 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/webmcp.d.ts +2 -0
  59. package/dist/webmcp.js +73 -0
  60. package/dist/webmcp.js.map +1 -0
  61. package/package.json +26 -0
  62. package/src/browser/controller.ts +984 -0
  63. package/src/browser/dom.ts +507 -0
  64. package/src/browser/feedback.ts +5 -0
  65. package/src/browser/locator.ts +383 -0
  66. package/src/browser/prompt.generated.ts +50 -0
  67. package/src/browser/prompt.ts +10 -0
  68. package/src/browser/snapshot.ts +542 -0
  69. package/src/browser/toolset.ts +522 -0
  70. package/src/browser/types.ts +172 -0
  71. package/src/browser/worker-runner.ts +468 -0
  72. package/src/definition.ts +115 -0
  73. package/src/index.ts +35 -0
  74. package/src/internal-react.tsx +62 -0
  75. package/src/internal-types.ts +12 -0
  76. package/src/internal.ts +36 -0
  77. package/src/react.tsx +397 -0
  78. package/src/runtime.ts +722 -0
  79. package/src/schema.ts +52 -0
  80. package/src/types.ts +123 -0
  81. package/src/webmcp.ts +102 -0
  82. package/tsconfig.json +8 -0
package/src/schema.ts ADDED
@@ -0,0 +1,52 @@
1
+ import type { JsonSchema } from "./types.js";
2
+
3
+ function typeMatches(schema: JsonSchema, value: unknown): boolean {
4
+ switch (schema.type) {
5
+ case undefined:
6
+ return true;
7
+ case "null":
8
+ return value === null;
9
+ case "array":
10
+ return Array.isArray(value);
11
+ case "object":
12
+ return value !== null && typeof value === "object" && !Array.isArray(value);
13
+ case "integer":
14
+ return typeof value === "number" && Number.isInteger(value);
15
+ default:
16
+ return typeof value === schema.type;
17
+ }
18
+ }
19
+
20
+ export function validateAgentToolInput(
21
+ schema: JsonSchema,
22
+ value: unknown,
23
+ path = "input",
24
+ ): string[] {
25
+ if (!typeMatches(schema, value)) {
26
+ return [`${path} must be ${schema.type}.`];
27
+ }
28
+ if (schema.enum && !schema.enum.some((item) => Object.is(item, value))) {
29
+ return [`${path} must be one of the declared enum values.`];
30
+ }
31
+ if (schema.type === "array" && Array.isArray(value) && schema.items) {
32
+ return value.flatMap((item, index) =>
33
+ validateAgentToolInput(schema.items!, item, `${path}[${index}]`),
34
+ );
35
+ }
36
+ if (schema.type !== "object" || value === null || typeof value !== "object") {
37
+ return [];
38
+ }
39
+ const record = value as Record<string, unknown>;
40
+ const errors: string[] = [];
41
+ for (const key of schema.required ?? []) {
42
+ if (!(key in record)) errors.push(`${path}.${key} is required.`);
43
+ }
44
+ for (const [key, item] of Object.entries(record)) {
45
+ const property = schema.properties?.[key];
46
+ if (property) errors.push(...validateAgentToolInput(property, item, `${path}.${key}`));
47
+ else if (schema.additionalProperties === false) {
48
+ errors.push(`${path}.${key} is not allowed.`);
49
+ }
50
+ }
51
+ return errors;
52
+ }
package/src/types.ts ADDED
@@ -0,0 +1,123 @@
1
+ export type JsonSchema = {
2
+ type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
3
+ description?: string;
4
+ properties?: Record<string, JsonSchema>;
5
+ required?: readonly string[];
6
+ items?: JsonSchema;
7
+ enum?: readonly unknown[];
8
+ additionalProperties?: boolean;
9
+ };
10
+
11
+ export type AgentToolInputSchema = JsonSchema & {
12
+ type: "object";
13
+ };
14
+
15
+ type Simplify<TValue> = { [TKey in keyof TValue]: TValue[TKey] };
16
+
17
+ type RequiredPropertyKeys<
18
+ TSchema extends JsonSchema,
19
+ TProperties extends Record<string, JsonSchema>,
20
+ > = TSchema extends { readonly required: readonly (infer TKey)[] }
21
+ ? Extract<TKey, keyof TProperties>
22
+ : never;
23
+
24
+ type InferObjectSchema<
25
+ TSchema extends JsonSchema,
26
+ TProperties extends Record<string, JsonSchema>,
27
+ > = keyof TProperties extends never
28
+ ? TSchema extends { readonly additionalProperties: false }
29
+ ? Record<string, never>
30
+ : Record<string, unknown>
31
+ : Simplify<{
32
+ [TKey in RequiredPropertyKeys<TSchema, TProperties>]-?: InferJsonSchema<
33
+ TProperties[TKey]
34
+ >;
35
+ } & {
36
+ [TKey in Exclude<
37
+ keyof TProperties,
38
+ RequiredPropertyKeys<TSchema, TProperties>
39
+ >]?: InferJsonSchema<TProperties[TKey]>;
40
+ }> & (TSchema extends { readonly additionalProperties: false }
41
+ ? unknown
42
+ : Record<string, unknown>);
43
+
44
+ export type InferJsonSchema<TSchema extends JsonSchema> =
45
+ TSchema extends { readonly enum: readonly (infer TValue)[] }
46
+ ? TValue
47
+ : TSchema extends { readonly type: "string" }
48
+ ? string
49
+ : TSchema extends { readonly type: "number" | "integer" }
50
+ ? number
51
+ : TSchema extends { readonly type: "boolean" }
52
+ ? boolean
53
+ : TSchema extends { readonly type: "null" }
54
+ ? null
55
+ : TSchema extends {
56
+ readonly type: "array";
57
+ readonly items: infer TItem extends JsonSchema;
58
+ }
59
+ ? InferJsonSchema<TItem>[]
60
+ : TSchema extends { readonly type: "array" }
61
+ ? unknown[]
62
+ : TSchema extends {
63
+ readonly type: "object";
64
+ readonly properties: infer TProperties extends Record<
65
+ string,
66
+ JsonSchema
67
+ >;
68
+ }
69
+ ? InferObjectSchema<TSchema, TProperties>
70
+ : TSchema extends { readonly type: "object" }
71
+ ? TSchema extends { readonly additionalProperties: false }
72
+ ? Record<string, never>
73
+ : Record<string, unknown>
74
+ : unknown;
75
+
76
+ export type AgentContextValue =
77
+ | string
78
+ | number
79
+ | boolean
80
+ | null
81
+ | readonly AgentContextValue[]
82
+ | { readonly [key: string]: AgentContextValue };
83
+
84
+ export interface AgentContextEntry {
85
+ name: string;
86
+ description: string;
87
+ value: AgentContextValue;
88
+ }
89
+
90
+ export type AgentContextInput = AgentContextEntry;
91
+
92
+ export interface AgentContextRegistration {
93
+ update(context: AgentContextInput): void;
94
+ dispose(): void;
95
+ }
96
+
97
+ export interface AgentToolMetadata {
98
+ name: string;
99
+ description: string;
100
+ inputSchema: AgentToolInputSchema;
101
+ }
102
+
103
+ export interface AgentToolDefinition<
104
+ TInput = Record<string, unknown>,
105
+ TResult = unknown,
106
+ >
107
+ extends AgentToolMetadata {
108
+ readonly __input?: TInput;
109
+ readonly __result?: TResult;
110
+ }
111
+
112
+ export interface AgentToolExecutionContext {
113
+ signal: AbortSignal;
114
+ waitForTool(
115
+ definition: AgentToolDefinition<any, any>,
116
+ options?: { timeoutMs?: number },
117
+ ): Promise<void>;
118
+ }
119
+
120
+ export type AgentToolExecutor<TInput, TResult> = (
121
+ input: TInput,
122
+ context: AgentToolExecutionContext,
123
+ ) => TResult | Promise<TResult>;
package/src/webmcp.ts ADDED
@@ -0,0 +1,102 @@
1
+ import type { InternalToolRuntime } from "./runtime.js";
2
+ import type { AgentToolDefinition } from "./types.js";
3
+
4
+ interface WebMcpTool {
5
+ name: string;
6
+ description: string;
7
+ inputSchema: object;
8
+ execute(input: object): Promise<unknown>;
9
+ }
10
+
11
+ interface WebMcpModelContext {
12
+ addEventListener?(type: "toolchange", listener: EventListener): void;
13
+ removeEventListener?(type: "toolchange", listener: EventListener): void;
14
+ registerTool(
15
+ tool: WebMcpTool,
16
+ options?: { signal?: AbortSignal },
17
+ ): Promise<void>;
18
+ }
19
+
20
+ type WebMcpRegistration = {
21
+ controller: AbortController;
22
+ definition: AgentToolDefinition;
23
+ };
24
+
25
+ function getWebMcpModelContext(): WebMcpModelContext | undefined {
26
+ if (typeof document === "undefined") return undefined;
27
+ const modelContext = (document as Document & {
28
+ modelContext?: Partial<WebMcpModelContext>;
29
+ }).modelContext;
30
+ return typeof modelContext?.registerTool === "function"
31
+ ? modelContext as WebMcpModelContext
32
+ : undefined;
33
+ }
34
+
35
+ function warnWebMcpRegistration(name: string, error: unknown): void {
36
+ console.warn(`Unable to register WebMCP Tool "${name}".`, error);
37
+ }
38
+
39
+ export function connectToolRuntimeToWebMcp(
40
+ runtime: InternalToolRuntime,
41
+ ): () => void {
42
+ const modelContext = getWebMcpModelContext();
43
+ if (!modelContext) return () => undefined;
44
+
45
+ const registrations = new Map<string, WebMcpRegistration>();
46
+ let disposed = false;
47
+
48
+ const register = (definition: AgentToolDefinition) => {
49
+ const controller = new AbortController();
50
+ const registration = { controller, definition };
51
+ registrations.set(definition.name, registration);
52
+ let result: Promise<void>;
53
+ try {
54
+ result = modelContext.registerTool({
55
+ name: definition.name,
56
+ description: definition.description,
57
+ inputSchema: definition.inputSchema,
58
+ execute: async (input) => runtime.execute(definition, input),
59
+ }, { signal: controller.signal });
60
+ } catch (error) {
61
+ registrations.delete(definition.name);
62
+ warnWebMcpRegistration(definition.name, error);
63
+ return;
64
+ }
65
+ Promise.resolve(result).catch((error: unknown) => {
66
+ if (controller.signal.aborted || disposed) return;
67
+ if (registrations.get(definition.name) === registration) {
68
+ registrations.delete(definition.name);
69
+ }
70
+ warnWebMcpRegistration(definition.name, error);
71
+ });
72
+ };
73
+
74
+ const reconcile = () => {
75
+ const definitions = new Map(
76
+ runtime.getApplicationTools().map((definition) => [definition.name, definition]),
77
+ );
78
+ for (const [name, registration] of registrations) {
79
+ if (definitions.get(name) === registration.definition) continue;
80
+ registration.controller.abort();
81
+ registrations.delete(name);
82
+ }
83
+ for (const definition of definitions.values()) {
84
+ if (!registrations.has(definition.name)) register(definition);
85
+ }
86
+ };
87
+
88
+ runtime.addEventListener("toolchange", reconcile);
89
+ modelContext.addEventListener?.("toolchange", reconcile);
90
+ reconcile();
91
+
92
+ return () => {
93
+ if (disposed) return;
94
+ disposed = true;
95
+ runtime.removeEventListener("toolchange", reconcile);
96
+ modelContext.removeEventListener?.("toolchange", reconcile);
97
+ for (const registration of registrations.values()) {
98
+ registration.controller.abort();
99
+ }
100
+ registrations.clear();
101
+ };
102
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
8
+ }