kly 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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/ai/context.mjs +79 -0
  4. package/dist/ai/context.mjs.map +1 -0
  5. package/dist/ai/storage.mjs +50 -0
  6. package/dist/ai/storage.mjs.map +1 -0
  7. package/dist/bin/kly.d.mts +1 -0
  8. package/dist/bin/kly.mjs +2888 -0
  9. package/dist/bin/kly.mjs.map +1 -0
  10. package/dist/bin/launcher-vTpgdO9n.mjs +3 -0
  11. package/dist/bin/permissions-2r_7ZqaH.mjs +3 -0
  12. package/dist/cli.mjs +229 -0
  13. package/dist/cli.mjs.map +1 -0
  14. package/dist/define-app.d.mts +33 -0
  15. package/dist/define-app.d.mts.map +1 -0
  16. package/dist/define-app.mjs +183 -0
  17. package/dist/define-app.mjs.map +1 -0
  18. package/dist/index.d.mts +16 -0
  19. package/dist/index.mjs +15 -0
  20. package/dist/mcp/index.mjs +4 -0
  21. package/dist/mcp/schema-converter.d.mts +13 -0
  22. package/dist/mcp/schema-converter.d.mts.map +1 -0
  23. package/dist/mcp/schema-converter.mjs +30 -0
  24. package/dist/mcp/schema-converter.mjs.map +1 -0
  25. package/dist/mcp/server.d.mts +33 -0
  26. package/dist/mcp/server.d.mts.map +1 -0
  27. package/dist/mcp/server.mjs +92 -0
  28. package/dist/mcp/server.mjs.map +1 -0
  29. package/dist/permissions/index.mjs +123 -0
  30. package/dist/permissions/index.mjs.map +1 -0
  31. package/dist/sandbox/bundled-executor.d.mts +17 -0
  32. package/dist/sandbox/bundled-executor.d.mts.map +1 -0
  33. package/dist/sandbox/bundled-executor.mjs +175 -0
  34. package/dist/sandbox/bundled-executor.mjs.map +1 -0
  35. package/dist/sandbox/ipc-client.mjs +40 -0
  36. package/dist/sandbox/ipc-client.mjs.map +1 -0
  37. package/dist/sandbox/sandboxed-context.mjs +14 -0
  38. package/dist/sandbox/sandboxed-context.mjs.map +1 -0
  39. package/dist/shared/constants.mjs +36 -0
  40. package/dist/shared/constants.mjs.map +1 -0
  41. package/dist/shared/runtime-mode.mjs +59 -0
  42. package/dist/shared/runtime-mode.mjs.map +1 -0
  43. package/dist/tool.d.mts +42 -0
  44. package/dist/tool.d.mts.map +1 -0
  45. package/dist/tool.mjs +38 -0
  46. package/dist/tool.mjs.map +1 -0
  47. package/dist/types.d.mts +282 -0
  48. package/dist/types.d.mts.map +1 -0
  49. package/dist/types.mjs +19 -0
  50. package/dist/types.mjs.map +1 -0
  51. package/dist/ui/components/confirm.d.mts +13 -0
  52. package/dist/ui/components/confirm.d.mts.map +1 -0
  53. package/dist/ui/components/confirm.mjs +37 -0
  54. package/dist/ui/components/confirm.mjs.map +1 -0
  55. package/dist/ui/components/form.d.mts +50 -0
  56. package/dist/ui/components/form.d.mts.map +1 -0
  57. package/dist/ui/components/form.mjs +92 -0
  58. package/dist/ui/components/form.mjs.map +1 -0
  59. package/dist/ui/components/input.d.mts +29 -0
  60. package/dist/ui/components/input.d.mts.map +1 -0
  61. package/dist/ui/components/input.mjs +42 -0
  62. package/dist/ui/components/input.mjs.map +1 -0
  63. package/dist/ui/components/select.d.mts +41 -0
  64. package/dist/ui/components/select.d.mts.map +1 -0
  65. package/dist/ui/components/select.mjs +50 -0
  66. package/dist/ui/components/select.mjs.map +1 -0
  67. package/dist/ui/components/spinner.d.mts +28 -0
  68. package/dist/ui/components/spinner.d.mts.map +1 -0
  69. package/dist/ui/components/spinner.mjs +35 -0
  70. package/dist/ui/components/spinner.mjs.map +1 -0
  71. package/dist/ui/components/table.d.mts +60 -0
  72. package/dist/ui/components/table.d.mts.map +1 -0
  73. package/dist/ui/components/table.mjs +143 -0
  74. package/dist/ui/components/table.mjs.map +1 -0
  75. package/dist/ui/index.d.mts +9 -0
  76. package/dist/ui/utils/colors.d.mts +38 -0
  77. package/dist/ui/utils/colors.d.mts.map +1 -0
  78. package/dist/ui/utils/colors.mjs +64 -0
  79. package/dist/ui/utils/colors.mjs.map +1 -0
  80. package/dist/ui/utils/output.d.mts +23 -0
  81. package/dist/ui/utils/output.d.mts.map +1 -0
  82. package/dist/ui/utils/output.mjs +42 -0
  83. package/dist/ui/utils/output.mjs.map +1 -0
  84. package/dist/ui/utils/tty.d.mts +9 -0
  85. package/dist/ui/utils/tty.d.mts.map +1 -0
  86. package/dist/ui/utils/tty.mjs +12 -0
  87. package/dist/ui/utils/tty.mjs.map +1 -0
  88. package/package.json +81 -0
@@ -0,0 +1,59 @@
1
+ import { ENV_VARS } from "./constants.mjs";
2
+
3
+ //#region src/shared/runtime-mode.ts
4
+ /**
5
+ * Check if running in sandbox mode
6
+ * Sandbox mode: Isolated child process with restricted permissions
7
+ */
8
+ function isSandbox() {
9
+ return process.env[ENV_VARS.SANDBOX_MODE] === "true";
10
+ }
11
+ /**
12
+ * Check if running in MCP (Model Context Protocol) mode
13
+ * MCP mode: Running as an MCP server for Claude Desktop integration
14
+ */
15
+ function isMCP() {
16
+ return process.env[ENV_VARS.MCP_MODE] === "true";
17
+ }
18
+ /**
19
+ * Check if running in programmatic mode
20
+ * Programmatic mode: Imported as a library in another application
21
+ */
22
+ function isProgrammatic() {
23
+ return process.env[ENV_VARS.PROGRAMMATIC] === "true";
24
+ }
25
+ /**
26
+ * Check if running with trust all flag
27
+ * Trust all: Skip permission prompts (for testing/automation)
28
+ */
29
+ function isTrustAll() {
30
+ return process.env[ENV_VARS.TRUST_ALL] === "true";
31
+ }
32
+ /**
33
+ * Get local reference environment variable
34
+ */
35
+ function getLocalRef() {
36
+ return process.env[ENV_VARS.LOCAL_REF];
37
+ }
38
+ /**
39
+ * Get remote reference environment variable
40
+ */
41
+ function getRemoteRef() {
42
+ return process.env[ENV_VARS.REMOTE_REF];
43
+ }
44
+ /**
45
+ * Detect the current runtime mode
46
+ * This is the canonical implementation that should be used throughout the app
47
+ */
48
+ function detectMode() {
49
+ if (isSandbox()) return "cli";
50
+ if (isMCP()) return "mcp";
51
+ if (isProgrammatic()) return "programmatic";
52
+ const scriptPath = process.argv[1] ?? "";
53
+ if (scriptPath.endsWith(".ts") || scriptPath.endsWith(".js")) return "cli";
54
+ return "programmatic";
55
+ }
56
+
57
+ //#endregion
58
+ export { detectMode, getLocalRef, getRemoteRef, isMCP, isSandbox, isTrustAll };
59
+ //# sourceMappingURL=runtime-mode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-mode.mjs","names":[],"sources":["../../src/shared/runtime-mode.ts"],"sourcesContent":["/**\n * Runtime mode detection utilities\n * Centralizes environment variable checks for different execution modes\n */\n\nimport type { RuntimeMode } from \"../types\";\nimport { ENV_VARS } from \"./constants\";\n\n/**\n * Check if running in sandbox mode\n * Sandbox mode: Isolated child process with restricted permissions\n */\nexport function isSandbox(): boolean {\n return process.env[ENV_VARS.SANDBOX_MODE] === \"true\";\n}\n\n/**\n * Check if running in MCP (Model Context Protocol) mode\n * MCP mode: Running as an MCP server for Claude Desktop integration\n */\nexport function isMCP(): boolean {\n return process.env[ENV_VARS.MCP_MODE] === \"true\";\n}\n\n/**\n * Check if running in programmatic mode\n * Programmatic mode: Imported as a library in another application\n */\nexport function isProgrammatic(): boolean {\n return process.env[ENV_VARS.PROGRAMMATIC] === \"true\";\n}\n\n/**\n * Check if running with trust all flag\n * Trust all: Skip permission prompts (for testing/automation)\n */\nexport function isTrustAll(): boolean {\n return process.env[ENV_VARS.TRUST_ALL] === \"true\";\n}\n\n/**\n * Get local reference environment variable\n */\nexport function getLocalRef(): string | undefined {\n return process.env[ENV_VARS.LOCAL_REF];\n}\n\n/**\n * Get remote reference environment variable\n */\nexport function getRemoteRef(): string | undefined {\n return process.env[ENV_VARS.REMOTE_REF];\n}\n\n/**\n * Detect the current runtime mode\n * This is the canonical implementation that should be used throughout the app\n */\nexport function detectMode(): RuntimeMode {\n // Sandbox mode: Running inside sandboxed child process\n if (isSandbox()) {\n return \"cli\"; // Sandbox always runs in CLI mode\n }\n\n // MCP mode: Check for MCP environment variable\n if (isMCP()) {\n return \"mcp\";\n }\n\n // Programmatic mode: Check for explicit flag\n if (isProgrammatic()) {\n return \"programmatic\";\n }\n\n // CLI mode: Running a .ts file directly with bun\n const scriptPath = process.argv[1] ?? \"\";\n const isDirectRun = scriptPath.endsWith(\".ts\") || scriptPath.endsWith(\".js\");\n if (isDirectRun) {\n return \"cli\";\n }\n\n // Default to programmatic (e.g., when imported as a module)\n return \"programmatic\";\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,YAAqB;AACnC,QAAO,QAAQ,IAAI,SAAS,kBAAkB;;;;;;AAOhD,SAAgB,QAAiB;AAC/B,QAAO,QAAQ,IAAI,SAAS,cAAc;;;;;;AAO5C,SAAgB,iBAA0B;AACxC,QAAO,QAAQ,IAAI,SAAS,kBAAkB;;;;;;AAOhD,SAAgB,aAAsB;AACpC,QAAO,QAAQ,IAAI,SAAS,eAAe;;;;;AAM7C,SAAgB,cAAkC;AAChD,QAAO,QAAQ,IAAI,SAAS;;;;;AAM9B,SAAgB,eAAmC;AACjD,QAAO,QAAQ,IAAI,SAAS;;;;;;AAO9B,SAAgB,aAA0B;AAExC,KAAI,WAAW,CACb,QAAO;AAIT,KAAI,OAAO,CACT,QAAO;AAIT,KAAI,gBAAgB,CAClB,QAAO;CAIT,MAAM,aAAa,QAAQ,KAAK,MAAM;AAEtC,KADoB,WAAW,SAAS,MAAM,IAAI,WAAW,SAAS,MAAM,CAE1E,QAAO;AAIT,QAAO"}
@@ -0,0 +1,42 @@
1
+ import { StandardSchemaV1, Tool, ToolDefinition } from "./types.mjs";
2
+
3
+ //#region src/tool.d.ts
4
+
5
+ /**
6
+ * Tool definition input with required name
7
+ */
8
+ interface ToolInput<TInput extends StandardSchemaV1, TResult = unknown> extends ToolDefinition<TInput, TResult> {
9
+ /** Tool name (used as subcommand in CLI, tool name in MCP) */
10
+ name: string;
11
+ }
12
+ /**
13
+ * Create a type-safe tool definition
14
+ *
15
+ * This is a helper function that enables TypeScript to infer the types
16
+ * of execute arguments from the inputSchema. Without this helper,
17
+ * TypeScript cannot establish the connection between schema and execute.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { tool } from "kly"
22
+ * import { z } from "zod"
23
+ *
24
+ * const weatherTool = tool({
25
+ * name: "weather",
26
+ * description: "Get weather for a location",
27
+ * inputSchema: z.object({
28
+ * city: z.string().describe("City name"),
29
+ * unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
30
+ * }),
31
+ * execute: async ({ city, unit }) => {
32
+ * // city is inferred as string
33
+ * // unit is inferred as "celsius" | "fahrenheit"
34
+ * return fetchWeather(city, unit)
35
+ * },
36
+ * })
37
+ * ```
38
+ */
39
+ declare function tool<TInput extends StandardSchemaV1, TResult = unknown>(definition: ToolInput<TInput, TResult>): Tool<TInput, TResult>;
40
+ //#endregion
41
+ export { tool };
42
+ //# sourceMappingURL=tool.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.mts","names":[],"sources":["../src/tool.ts"],"sourcesContent":[],"mappings":";;;;;;AAAsE;UAK5D,SAAyB,CAAA,eAAA,gBAAA,EAAA,UAAA,OAAA,CAAA,SACzB,cADyB,CACV,MADU,EACF,OADE,CAAA,CAAA;EACV;EAAQ,IAAA,EAAA,MAAA;;;AAgCjC;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,oBAAoB,iDACtB,UAAU,QAAQ,WAC7B,KAAK,QAAQ"}
package/dist/tool.mjs ADDED
@@ -0,0 +1,38 @@
1
+ //#region src/tool.ts
2
+ /**
3
+ * Create a type-safe tool definition
4
+ *
5
+ * This is a helper function that enables TypeScript to infer the types
6
+ * of execute arguments from the inputSchema. Without this helper,
7
+ * TypeScript cannot establish the connection between schema and execute.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { tool } from "kly"
12
+ * import { z } from "zod"
13
+ *
14
+ * const weatherTool = tool({
15
+ * name: "weather",
16
+ * description: "Get weather for a location",
17
+ * inputSchema: z.object({
18
+ * city: z.string().describe("City name"),
19
+ * unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
20
+ * }),
21
+ * execute: async ({ city, unit }) => {
22
+ * // city is inferred as string
23
+ * // unit is inferred as "celsius" | "fahrenheit"
24
+ * return fetchWeather(city, unit)
25
+ * },
26
+ * })
27
+ * ```
28
+ */
29
+ function tool(definition) {
30
+ return {
31
+ ...definition,
32
+ _brand: "Tool"
33
+ };
34
+ }
35
+
36
+ //#endregion
37
+ export { tool };
38
+ //# sourceMappingURL=tool.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.mjs","names":[],"sources":["../src/tool.ts"],"sourcesContent":["import type { StandardSchemaV1, Tool, ToolDefinition } from \"./types\";\n\n/**\n * Tool definition input with required name\n */\ninterface ToolInput<TInput extends StandardSchemaV1, TResult = unknown>\n extends ToolDefinition<TInput, TResult> {\n /** Tool name (used as subcommand in CLI, tool name in MCP) */\n name: string;\n}\n\n/**\n * Create a type-safe tool definition\n *\n * This is a helper function that enables TypeScript to infer the types\n * of execute arguments from the inputSchema. Without this helper,\n * TypeScript cannot establish the connection between schema and execute.\n *\n * @example\n * ```typescript\n * import { tool } from \"kly\"\n * import { z } from \"zod\"\n *\n * const weatherTool = tool({\n * name: \"weather\",\n * description: \"Get weather for a location\",\n * inputSchema: z.object({\n * city: z.string().describe(\"City name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).default(\"celsius\"),\n * }),\n * execute: async ({ city, unit }) => {\n * // city is inferred as string\n * // unit is inferred as \"celsius\" | \"fahrenheit\"\n * return fetchWeather(city, unit)\n * },\n * })\n * ```\n */\nexport function tool<TInput extends StandardSchemaV1, TResult = unknown>(\n definition: ToolInput<TInput, TResult>,\n): Tool<TInput, TResult> {\n return {\n ...definition,\n _brand: \"Tool\" as const,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,KACd,YACuB;AACvB,QAAO;EACL,GAAG;EACH,QAAQ;EACT"}
@@ -0,0 +1,282 @@
1
+ import { SandboxRuntimeConfig } from "@anthropic-ai/sandbox-runtime";
2
+
3
+ //#region src/types.d.ts
4
+
5
+ /**
6
+ * Standard Schema V1 interface (inlined to avoid external dependency)
7
+ * @see https://github.com/standard-schema/standard-schema
8
+ */
9
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
10
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
11
+ }
12
+ declare namespace StandardSchemaV1 {
13
+ interface Props<Input = unknown, Output = Input> {
14
+ readonly version: 1;
15
+ readonly vendor: string;
16
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
17
+ readonly types?: Types<Input, Output>;
18
+ }
19
+ type Result<Output> = SuccessResult<Output> | FailureResult;
20
+ interface SuccessResult<Output> {
21
+ readonly value: Output;
22
+ readonly issues?: undefined;
23
+ }
24
+ interface FailureResult {
25
+ readonly issues: ReadonlyArray<Issue>;
26
+ }
27
+ interface Issue {
28
+ readonly message: string;
29
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment>;
30
+ }
31
+ interface PathSegment {
32
+ readonly key: PropertyKey;
33
+ }
34
+ interface Types<Input = unknown, Output = Input> {
35
+ readonly input: Input;
36
+ readonly output: Output;
37
+ }
38
+ }
39
+ /**
40
+ * Extract output type from a Standard Schema
41
+ */
42
+ type InferOutput<TSchema> = TSchema extends StandardSchemaV1<unknown, infer Output> ? Output : never;
43
+ /**
44
+ * Runtime execution mode
45
+ */
46
+ type RuntimeMode = "cli" | "mcp" | "programmatic";
47
+ /**
48
+ * Model configuration information (basic info without secrets)
49
+ */
50
+ interface ModelInfo {
51
+ /** Model configuration name */
52
+ name: string;
53
+ /** Provider (openai, anthropic, etc.) */
54
+ provider: string;
55
+ /** Model name (e.g., gpt-4, claude-3-opus) */
56
+ model?: string;
57
+ /** Whether this is the current active model */
58
+ isCurrent: boolean;
59
+ }
60
+ /**
61
+ * Full model configuration including API keys
62
+ */
63
+ interface ModelConfig {
64
+ /** Provider (openai, anthropic, etc.) */
65
+ provider: string;
66
+ /** Model name (e.g., gpt-4, claude-3-opus) */
67
+ model?: string;
68
+ /** API key */
69
+ apiKey?: string;
70
+ /** Base URL */
71
+ baseURL?: string;
72
+ }
73
+ /**
74
+ * Models management interface
75
+ * Provides access to configured LLM models and their credentials
76
+ */
77
+ interface ModelsContext {
78
+ /**
79
+ * List all configured models (without API keys)
80
+ */
81
+ list(): ModelInfo[];
82
+ /**
83
+ * Get the current active model info
84
+ */
85
+ getCurrent(): ModelInfo | null;
86
+ /**
87
+ * Get a specific model info by name
88
+ */
89
+ get(name: string): ModelInfo | null;
90
+ /**
91
+ * Get full configuration for a model (including API key)
92
+ *
93
+ * ⚠️ SECURITY: This method requires user permission
94
+ * The first time this is called, the user will be prompted to grant permission.
95
+ * Permission can be granted for:
96
+ * - One time only
97
+ * - Always for this app
98
+ * - Denied
99
+ *
100
+ * @param name - Model name (if not specified, returns current model)
101
+ * @returns Full config or null if not found/configured
102
+ * @throws Error if permission is denied
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const config = await context.models.getConfigAsync();
107
+ * if (config) {
108
+ * // Use config.provider, config.apiKey, config.model etc.
109
+ * }
110
+ * ```
111
+ */
112
+ getConfigAsync(name?: string): Promise<ModelConfig | null>;
113
+ }
114
+ /**
115
+ * Context passed to the execute function
116
+ */
117
+ interface ExecuteContext {
118
+ /** Current runtime mode */
119
+ mode: RuntimeMode;
120
+ /** Abort signal for cancellation */
121
+ abortSignal?: AbortSignal;
122
+ /**
123
+ * Models management
124
+ * Access configured LLM models and their credentials
125
+ * @example
126
+ * ```typescript
127
+ * const config = context.models.getConfig();
128
+ * if (config) {
129
+ * // Use config.provider, config.apiKey, config.model etc.
130
+ * // to create your own AI SDK provider instance
131
+ * }
132
+ * ```
133
+ */
134
+ models: ModelsContext;
135
+ }
136
+ /**
137
+ * Tool definition (Vercel AI SDK style)
138
+ * Core building block that can be used standalone or within defineApp
139
+ */
140
+ interface ToolDefinition<TInput extends StandardSchemaV1, TResult = unknown> {
141
+ /** Description of what the tool does (used by LLM for tool selection) */
142
+ description?: string;
143
+ /** Input schema (Standard Schema compliant: Zod, Valibot, ArkType, etc.) */
144
+ inputSchema: TInput;
145
+ /** Execute function that performs the tool's action */
146
+ execute: (args: InferOutput<TInput>, context: ExecuteContext) => Promise<TResult>;
147
+ }
148
+ /**
149
+ * Tool instance returned by tool()
150
+ * Each tool has a name for CLI subcommand and MCP registration
151
+ */
152
+ interface Tool<TInput extends StandardSchemaV1, TResult = unknown> extends ToolDefinition<TInput, TResult> {
153
+ /** Tool name (used as subcommand in CLI, tool name in MCP) */
154
+ name: string;
155
+ /** Type brand for tool identification */
156
+ readonly _brand: "Tool";
157
+ }
158
+ /**
159
+ * Any tool type for collections
160
+ */
161
+ type AnyTool = Tool<StandardSchemaV1, unknown>;
162
+ /**
163
+ * Base app metadata
164
+ */
165
+ interface AppMetadata {
166
+ /** App name */
167
+ name: string;
168
+ /** Semantic version */
169
+ version: string;
170
+ /** App description */
171
+ description: string;
172
+ }
173
+ /**
174
+ * App definition with tools array
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * defineApp({
179
+ * name: "my-app",
180
+ * version: "0.1.0",
181
+ * description: "My CLI app",
182
+ * tools: [greetTool, farewellTool],
183
+ * instructions: "When user expresses greeting intent, prefer the greet tool",
184
+ * })
185
+ * ```
186
+ */
187
+ /**
188
+ * App permissions declaration
189
+ * Simplified user-friendly interface for declaring app permissions
190
+ */
191
+ interface AppPermissions {
192
+ /**
193
+ * API Keys access - needed to call LLM APIs
194
+ *
195
+ * When enabled:
196
+ * - Grants access to models.getConfigAsync() to retrieve API keys
197
+ * - Automatically allows network access to common LLM API domains:
198
+ * • api.openai.com
199
+ * • *.anthropic.com
200
+ * • generativelanguage.googleapis.com
201
+ * • api.deepseek.com
202
+ *
203
+ * @default false
204
+ */
205
+ apiKeys?: boolean;
206
+ /**
207
+ * Sandbox configuration
208
+ * Uses @anthropic-ai/sandbox-runtime configuration directly
209
+ *
210
+ * Defaults when not specified:
211
+ * - Network: No access (apiKeys=true adds LLM APIs automatically)
212
+ * - Filesystem: Current directory read/write only
213
+ * - Protected: ~/.kly, ~/.ssh, ~/.aws, ~/.gnupg always denied
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * // Minimal: just need GitHub API access
218
+ * permissions: {
219
+ * sandbox: {
220
+ * network: { allowedDomains: ["api.github.com"] }
221
+ * }
222
+ * }
223
+ *
224
+ * // Full control
225
+ * permissions: {
226
+ * apiKeys: true, // LLM domains added automatically
227
+ * sandbox: {
228
+ * network: {
229
+ * allowedDomains: ["api.github.com"] // Add more domains
230
+ * },
231
+ * filesystem: {
232
+ * allowWrite: ["./output", "/tmp"],
233
+ * denyRead: ["/etc"]
234
+ * }
235
+ * }
236
+ * }
237
+ * ```
238
+ */
239
+ sandbox?: Partial<SandboxRuntimeConfig>;
240
+ }
241
+ interface AppDefinition<TTools extends AnyTool[] = AnyTool[]> extends AppMetadata {
242
+ /** Array of tools */
243
+ tools: TTools;
244
+ /**
245
+ * AI instructions for Skill mode
246
+ * Hints for AI routing when this app is composed with others
247
+ */
248
+ instructions?: string;
249
+ /**
250
+ * Permissions required by this app
251
+ * Declare upfront what your app needs to access
252
+ * @example
253
+ * ```typescript
254
+ * permissions: {
255
+ * apiKeys: true,
256
+ * network: { allow: "llm-apis" }
257
+ * }
258
+ * ```
259
+ */
260
+ permissions?: AppPermissions;
261
+ }
262
+ /**
263
+ * Kly app instance returned by defineApp
264
+ */
265
+ interface KlyApp<TTools extends AnyTool[] = AnyTool[]> {
266
+ /** Original app configuration */
267
+ readonly definition: AppDefinition<TTools>;
268
+ /** Execute a specific tool by name */
269
+ execute(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
270
+ /** Get all available tools */
271
+ readonly tools: Map<string, AnyTool>;
272
+ }
273
+ /**
274
+ * Validation error with structured issues
275
+ */
276
+ declare class ValidationError extends Error {
277
+ readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
278
+ constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>);
279
+ }
280
+ //#endregion
281
+ export { AnyTool, AppDefinition, AppPermissions, ExecuteContext, InferOutput, KlyApp, ModelConfig, ModelInfo, ModelsContext, RuntimeMode, StandardSchemaV1, Tool, ToolDefinition, ValidationError };
282
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/types.mjs ADDED
@@ -0,0 +1,19 @@
1
+ //#region src/types.ts
2
+ /**
3
+ * Validation error with structured issues
4
+ */
5
+ var ValidationError = class extends Error {
6
+ constructor(issues) {
7
+ const message = issues.map((issue) => {
8
+ const path = issue.path?.map((p) => typeof p === "object" ? p.key : p).join(".");
9
+ return path ? `${path}: ${issue.message}` : issue.message;
10
+ }).join("\n");
11
+ super(message);
12
+ this.issues = issues;
13
+ this.name = "ValidationError";
14
+ }
15
+ };
16
+
17
+ //#endregion
18
+ export { ValidationError };
19
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","names":["issues: ReadonlyArray<StandardSchemaV1.Issue>"],"sources":["../src/types.ts"],"sourcesContent":["import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\n\n/**\n * Standard Schema V1 interface (inlined to avoid external dependency)\n * @see https://github.com/standard-schema/standard-schema\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly \"~standard\": StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown,\n ) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output>;\n }\n\n type Result<Output> = SuccessResult<Output> | FailureResult;\n\n interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment>;\n }\n\n interface PathSegment {\n readonly key: PropertyKey;\n }\n\n interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n}\n\n/**\n * Extract output type from a Standard Schema\n */\nexport type InferOutput<TSchema> =\n TSchema extends StandardSchemaV1<unknown, infer Output> ? Output : never;\n\n/**\n * Runtime execution mode\n */\nexport type RuntimeMode = \"cli\" | \"mcp\" | \"programmatic\";\n\n/**\n * Model configuration information (basic info without secrets)\n */\nexport interface ModelInfo {\n /** Model configuration name */\n name: string;\n /** Provider (openai, anthropic, etc.) */\n provider: string;\n /** Model name (e.g., gpt-4, claude-3-opus) */\n model?: string;\n /** Whether this is the current active model */\n isCurrent: boolean;\n}\n\n/**\n * Full model configuration including API keys\n */\nexport interface ModelConfig {\n /** Provider (openai, anthropic, etc.) */\n provider: string;\n /** Model name (e.g., gpt-4, claude-3-opus) */\n model?: string;\n /** API key */\n apiKey?: string;\n /** Base URL */\n baseURL?: string;\n}\n\n/**\n * Models management interface\n * Provides access to configured LLM models and their credentials\n */\nexport interface ModelsContext {\n /**\n * List all configured models (without API keys)\n */\n list(): ModelInfo[];\n\n /**\n * Get the current active model info\n */\n getCurrent(): ModelInfo | null;\n\n /**\n * Get a specific model info by name\n */\n get(name: string): ModelInfo | null;\n\n /**\n * Get full configuration for a model (including API key)\n *\n * ⚠️ SECURITY: This method requires user permission\n * The first time this is called, the user will be prompted to grant permission.\n * Permission can be granted for:\n * - One time only\n * - Always for this app\n * - Denied\n *\n * @param name - Model name (if not specified, returns current model)\n * @returns Full config or null if not found/configured\n * @throws Error if permission is denied\n *\n * @example\n * ```typescript\n * const config = await context.models.getConfigAsync();\n * if (config) {\n * // Use config.provider, config.apiKey, config.model etc.\n * }\n * ```\n */\n getConfigAsync(name?: string): Promise<ModelConfig | null>;\n}\n\n/**\n * Context passed to the execute function\n */\nexport interface ExecuteContext {\n /** Current runtime mode */\n mode: RuntimeMode;\n /** Abort signal for cancellation */\n abortSignal?: AbortSignal;\n /**\n * Models management\n * Access configured LLM models and their credentials\n * @example\n * ```typescript\n * const config = context.models.getConfig();\n * if (config) {\n * // Use config.provider, config.apiKey, config.model etc.\n * // to create your own AI SDK provider instance\n * }\n * ```\n */\n models: ModelsContext;\n}\n\n/**\n * Tool definition (Vercel AI SDK style)\n * Core building block that can be used standalone or within defineApp\n */\nexport interface ToolDefinition<\n TInput extends StandardSchemaV1,\n TResult = unknown,\n> {\n /** Description of what the tool does (used by LLM for tool selection) */\n description?: string;\n /** Input schema (Standard Schema compliant: Zod, Valibot, ArkType, etc.) */\n inputSchema: TInput;\n /** Execute function that performs the tool's action */\n execute: (\n args: InferOutput<TInput>,\n context: ExecuteContext,\n ) => Promise<TResult>;\n}\n\n/**\n * Tool instance returned by tool()\n * Each tool has a name for CLI subcommand and MCP registration\n */\nexport interface Tool<TInput extends StandardSchemaV1, TResult = unknown>\n extends ToolDefinition<TInput, TResult> {\n /** Tool name (used as subcommand in CLI, tool name in MCP) */\n name: string;\n /** Type brand for tool identification */\n readonly _brand: \"Tool\";\n}\n\n/**\n * Any tool type for collections\n */\nexport type AnyTool = Tool<StandardSchemaV1, unknown>;\n\n/**\n * Base app metadata\n */\nexport interface AppMetadata {\n /** App name */\n name: string;\n /** Semantic version */\n version: string;\n /** App description */\n description: string;\n}\n\n/**\n * App definition with tools array\n *\n * @example\n * ```typescript\n * defineApp({\n * name: \"my-app\",\n * version: \"0.1.0\",\n * description: \"My CLI app\",\n * tools: [greetTool, farewellTool],\n * instructions: \"When user expresses greeting intent, prefer the greet tool\",\n * })\n * ```\n */\n/**\n * App permissions declaration\n * Simplified user-friendly interface for declaring app permissions\n */\nexport interface AppPermissions {\n /**\n * API Keys access - needed to call LLM APIs\n *\n * When enabled:\n * - Grants access to models.getConfigAsync() to retrieve API keys\n * - Automatically allows network access to common LLM API domains:\n * • api.openai.com\n * • *.anthropic.com\n * • generativelanguage.googleapis.com\n * • api.deepseek.com\n *\n * @default false\n */\n apiKeys?: boolean;\n\n /**\n * Sandbox configuration\n * Uses @anthropic-ai/sandbox-runtime configuration directly\n *\n * Defaults when not specified:\n * - Network: No access (apiKeys=true adds LLM APIs automatically)\n * - Filesystem: Current directory read/write only\n * - Protected: ~/.kly, ~/.ssh, ~/.aws, ~/.gnupg always denied\n *\n * @example\n * ```typescript\n * // Minimal: just need GitHub API access\n * permissions: {\n * sandbox: {\n * network: { allowedDomains: [\"api.github.com\"] }\n * }\n * }\n *\n * // Full control\n * permissions: {\n * apiKeys: true, // LLM domains added automatically\n * sandbox: {\n * network: {\n * allowedDomains: [\"api.github.com\"] // Add more domains\n * },\n * filesystem: {\n * allowWrite: [\"./output\", \"/tmp\"],\n * denyRead: [\"/etc\"]\n * }\n * }\n * }\n * ```\n */\n sandbox?: Partial<SandboxRuntimeConfig>;\n}\n\nexport interface AppDefinition<TTools extends AnyTool[] = AnyTool[]>\n extends AppMetadata {\n /** Array of tools */\n tools: TTools;\n /**\n * AI instructions for Skill mode\n * Hints for AI routing when this app is composed with others\n */\n instructions?: string;\n /**\n * Permissions required by this app\n * Declare upfront what your app needs to access\n * @example\n * ```typescript\n * permissions: {\n * apiKeys: true,\n * network: { allow: \"llm-apis\" }\n * }\n * ```\n */\n permissions?: AppPermissions;\n}\n\n/**\n * Kly app instance returned by defineApp\n */\nexport interface KlyApp<TTools extends AnyTool[] = AnyTool[]> {\n /** Original app configuration */\n readonly definition: AppDefinition<TTools>;\n /** Execute a specific tool by name */\n execute(toolName: string, args?: Record<string, unknown>): Promise<unknown>;\n /** Get all available tools */\n readonly tools: Map<string, AnyTool>;\n}\n\n/**\n * Validation error with structured issues\n */\nexport class ValidationError extends Error {\n constructor(public readonly issues: ReadonlyArray<StandardSchemaV1.Issue>) {\n const message = issues\n .map((issue) => {\n const path = issue.path\n ?.map((p) => (typeof p === \"object\" ? p.key : p))\n .join(\".\");\n return path ? `${path}: ${issue.message}` : issue.message;\n })\n .join(\"\\n\");\n super(message);\n this.name = \"ValidationError\";\n }\n}\n"],"mappings":";;;;AAqTA,IAAa,kBAAb,cAAqC,MAAM;CACzC,YAAY,AAAgBA,QAA+C;EACzE,MAAM,UAAU,OACb,KAAK,UAAU;GACd,MAAM,OAAO,MAAM,MACf,KAAK,MAAO,OAAO,MAAM,WAAW,EAAE,MAAM,EAAG,CAChD,KAAK,IAAI;AACZ,UAAO,OAAO,GAAG,KAAK,IAAI,MAAM,YAAY,MAAM;IAClD,CACD,KAAK,KAAK;AACb,QAAM,QAAQ;EATY;AAU1B,OAAK,OAAO"}
@@ -0,0 +1,13 @@
1
+ //#region src/ui/components/confirm.d.ts
2
+ /**
3
+ * Simplified confirm function
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const proceed = await confirm("Continue?", true);
8
+ * ```
9
+ */
10
+ declare function confirm(message: string, defaultValue?: boolean): Promise<boolean>;
11
+ //#endregion
12
+ export { confirm };
13
+ //# sourceMappingURL=confirm.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.d.mts","names":[],"sources":["../../../src/ui/components/confirm.ts"],"sourcesContent":[],"mappings":";;AAaA;;;;;;;iBAAsB,OAAA,2CAGnB"}
@@ -0,0 +1,37 @@
1
+ import { isMCP, isSandbox } from "../../shared/runtime-mode.mjs";
2
+ import { isTTY } from "../utils/tty.mjs";
3
+ import { sendIPCRequest } from "../../sandbox/ipc-client.mjs";
4
+ import * as p from "@clack/prompts";
5
+
6
+ //#region src/ui/components/confirm.ts
7
+ /**
8
+ * Simplified confirm function
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const proceed = await confirm("Continue?", true);
13
+ * ```
14
+ */
15
+ async function confirm(message, defaultValue = false) {
16
+ if (isSandbox()) return sendIPCRequest("prompt:confirm", {
17
+ message,
18
+ defaultValue
19
+ });
20
+ if (!isTTY()) {
21
+ if (isMCP()) console.warn(`[MCP Warning] Interactive confirmation not available. Using default value (${defaultValue}) for: ${message}`);
22
+ return defaultValue;
23
+ }
24
+ const result = await p.confirm({
25
+ message,
26
+ initialValue: defaultValue
27
+ });
28
+ if (p.isCancel(result)) {
29
+ p.cancel("Operation cancelled");
30
+ process.exit(0);
31
+ }
32
+ return result;
33
+ }
34
+
35
+ //#endregion
36
+ export { confirm };
37
+ //# sourceMappingURL=confirm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.mjs","names":[],"sources":["../../../src/ui/components/confirm.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Simplified confirm function\n *\n * @example\n * ```typescript\n * const proceed = await confirm(\"Continue?\", true);\n * ```\n */\nexport async function confirm(\n message: string,\n defaultValue = false,\n): Promise<boolean> {\n // Sandbox mode: use IPC to request confirm from host\n if (isSandbox()) {\n return sendIPCRequest<boolean>(\"prompt:confirm\", { message, defaultValue });\n }\n\n if (!isTTY()) {\n // In MCP mode, warn about using default value for confirmation\n if (isMCP()) {\n console.warn(\n `[MCP Warning] Interactive confirmation not available. Using default value (${defaultValue}) for: ${message}`,\n );\n }\n return defaultValue;\n }\n\n const result = await p.confirm({\n message,\n initialValue: defaultValue,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,eAAsB,QACpB,SACA,eAAe,OACG;AAElB,KAAI,WAAW,CACb,QAAO,eAAwB,kBAAkB;EAAE;EAAS;EAAc,CAAC;AAG7E,KAAI,CAAC,OAAO,EAAE;AAEZ,MAAI,OAAO,CACT,SAAQ,KACN,8EAA8E,aAAa,SAAS,UACrG;AAEH,SAAO;;CAGT,MAAM,SAAS,MAAM,EAAE,QAAQ;EAC7B;EACA,cAAc;EACf,CAAC;AAEF,KAAI,EAAE,SAAS,OAAO,EAAE;AACtB,IAAE,OAAO,sBAAsB;AAC/B,UAAQ,KAAK,EAAE;;AAGjB,QAAO"}
@@ -0,0 +1,50 @@
1
+ //#region src/ui/components/form.d.ts
2
+ /**
3
+ * Field definition for forms
4
+ */
5
+ interface FormField {
6
+ /** Field name/key */
7
+ name: string;
8
+ /** Display label */
9
+ label: string;
10
+ /** Field type */
11
+ type: "string" | "number" | "boolean" | "enum";
12
+ /** Whether field is required */
13
+ required?: boolean;
14
+ /** Default value */
15
+ defaultValue?: unknown;
16
+ /** Description/help text */
17
+ description?: string;
18
+ /** Enum options (for type: "enum") */
19
+ enumValues?: string[];
20
+ }
21
+ /**
22
+ * Configuration for form component
23
+ */
24
+ interface FormConfig {
25
+ /** Form fields */
26
+ fields: FormField[];
27
+ /** Form title */
28
+ title?: string;
29
+ }
30
+ /**
31
+ * Prompt for multiple fields sequentially
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const values = await form({
36
+ * title: "User Registration",
37
+ * fields: [
38
+ * { name: "name", label: "Your name", type: "string", required: true },
39
+ * { name: "age", label: "Your age", type: "number", defaultValue: 18 },
40
+ * { name: "role", label: "Role", type: "enum", enumValues: ["admin", "user"] },
41
+ * { name: "subscribe", label: "Subscribe to newsletter?", type: "boolean" }
42
+ * ]
43
+ * });
44
+ * // values: { name: string, age: number, role: string, subscribe: boolean }
45
+ * ```
46
+ */
47
+ declare function form(config: FormConfig): Promise<Record<string, unknown>>;
48
+ //#endregion
49
+ export { FormConfig, FormField, form };
50
+ //# sourceMappingURL=form.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.mts","names":[],"sources":["../../../src/ui/components/form.ts"],"sourcesContent":[],"mappings":";;AASA;AAoBA;AAwBsB,UA5CL,SAAA,CA4CS;EAChB;EACC,IAAA,EAAA,MAAA;EAAR;EAAO,KAAA,EAAA,MAAA;;;;;;;;;;;;;;;UA1BO,UAAA;;UAEP;;;;;;;;;;;;;;;;;;;;;iBAsBY,IAAA,SACZ,aACP,QAAQ"}