@agent-api/sdk 1.1.2 → 1.1.4
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 +37 -3
- package/dist/client.d.ts +3 -1
- package/dist/client.js +4 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -2
- package/dist/node-client.d.ts +6 -0
- package/dist/node-client.js +7 -0
- package/dist/node.d.ts +8 -0
- package/dist/node.js +6 -0
- package/dist/preset-tools.d.ts +53 -0
- package/dist/preset-tools.js +85 -0
- package/dist/resources/responses.js +2 -0
- package/dist/resources/skills-node.d.ts +7 -0
- package/dist/resources/skills-node.js +156 -0
- package/dist/resources/skills.d.ts +1 -3
- package/dist/resources/skills.js +0 -153
- package/dist/tool-validation.d.ts +2 -0
- package/dist/tool-validation.js +15 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist-cjs/client.js +71 -0
- package/dist-cjs/errors.js +146 -0
- package/dist-cjs/index.js +51 -0
- package/dist-cjs/internal/env.js +7 -0
- package/dist-cjs/internal/http.js +108 -0
- package/dist-cjs/internal/output-text.js +15 -0
- package/dist-cjs/internal/query.js +13 -0
- package/dist-cjs/local/context.js +158 -0
- package/dist-cjs/local/core.js +1077 -0
- package/dist-cjs/local/index.js +19 -0
- package/dist-cjs/local/tools.js +380 -0
- package/dist-cjs/local-functions.js +37 -0
- package/dist-cjs/local-skills.js +296 -0
- package/dist-cjs/node-client.js +11 -0
- package/dist-cjs/node.js +32 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/pagination.js +43 -0
- package/dist-cjs/preset-tools.js +91 -0
- package/dist-cjs/resources/auth.js +92 -0
- package/dist-cjs/resources/models.js +13 -0
- package/dist-cjs/resources/presets.js +13 -0
- package/dist-cjs/resources/responses.js +58 -0
- package/dist-cjs/resources/skills-node.js +193 -0
- package/dist-cjs/resources/skills.js +112 -0
- package/dist-cjs/resources/tools.js +13 -0
- package/dist-cjs/resources/volumes.js +114 -0
- package/dist-cjs/streaming.js +42 -0
- package/dist-cjs/tool-validation.js +18 -0
- package/dist-cjs/types/auth.js +2 -0
- package/dist-cjs/types/catalog.js +2 -0
- package/dist-cjs/types/common.js +2 -0
- package/dist-cjs/types/index.js +25 -0
- package/dist-cjs/types/input.js +2 -0
- package/dist-cjs/types/responses.js +2 -0
- package/dist-cjs/types/skills.js +2 -0
- package/dist-cjs/types/streaming.js +2 -0
- package/dist-cjs/types/tools.js +2 -0
- package/dist-cjs/types/volumes.js +2 -0
- package/dist-cjs/version.js +5 -0
- package/package.json +20 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Production JavaScript/TypeScript SDK for the Managed Agent API.
|
|
4
4
|
|
|
5
|
-
**Published on npm:** [`@agent-api/sdk`](https://www.npmjs.com/package/@agent-api/sdk) (v1.1.
|
|
5
|
+
**Published on npm:** [`@agent-api/sdk`](https://www.npmjs.com/package/@agent-api/sdk) (v1.1.4)
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -40,6 +40,13 @@ The default base URL is `https://api.agentsway.dev` when neither option nor env
|
|
|
40
40
|
|
|
41
41
|
## Package layout
|
|
42
42
|
|
|
43
|
+
Public package entrypoints:
|
|
44
|
+
|
|
45
|
+
- `@agent-api/sdk`: browser-safe REST client, public types, auth, responses, models, presets, tools, volumes, and skills HTTP APIs.
|
|
46
|
+
- `@agent-api/sdk/browser`: explicit alias of the browser-safe REST client entry.
|
|
47
|
+
- `@agent-api/sdk/local`: Node-only local runtime, workspace, context, and local workspace tool support.
|
|
48
|
+
- `@agent-api/sdk/node`: Node aggregate entry for local helpers such as `localSkillFromDirectory()` plus `NodeAgentAPI`.
|
|
49
|
+
|
|
43
50
|
```
|
|
44
51
|
src/
|
|
45
52
|
client.ts # AgentAPI entrypoint
|
|
@@ -60,9 +67,11 @@ src/
|
|
|
60
67
|
| `client.presets` | `list` |
|
|
61
68
|
| `client.tools` | `list` |
|
|
62
69
|
| `client.volumes` | `list`, `create`, `retrieve`, `update`, `delete`, `listEntries`, `searchEntries`, `readFile`, `writeFile`, `deletePath`, `reconcileUsage`, `createDirectory`, `downloadArchive`, `summarize`, `readLines`, `patchLines`, `grep` |
|
|
63
|
-
| `client.skills` | `list`, `create`, `discover`, `focus`, `createDev`, `updateFile`, `retrieve`, `update`, `archive`, `delete`, `diff`, `acceptDev`, `discardDev`, `exportArchive`, `importArchive`, `
|
|
70
|
+
| `client.skills` | `list`, `create`, `discover`, `focus`, `createDev`, `updateFile`, `retrieve`, `update`, `archive`, `delete`, `diff`, `acceptDev`, `discardDev`, `exportArchive`, `importArchive`, `listFiles`, `readFile`, `writeFile`, `deleteFile` |
|
|
64
71
|
| `client.auth` | `startDeviceAuth`, `pollDeviceAuth`, `waitForDeviceAuth` |
|
|
65
72
|
|
|
73
|
+
`NodeAgentAPI` from `@agent-api/sdk/node` extends `client.skills` with Node-only `pushDirectory` and `pullDirectory`.
|
|
74
|
+
|
|
66
75
|
## Browser Device Login
|
|
67
76
|
|
|
68
77
|
CLI and desktop apps can use browser login without handling user passwords or static API keys.
|
|
@@ -97,12 +106,37 @@ const response = await client.agent.create({
|
|
|
97
106
|
});
|
|
98
107
|
```
|
|
99
108
|
|
|
109
|
+
## Preset tools and local/client tools
|
|
110
|
+
|
|
111
|
+
`tools` is the concrete model-visible tool list. Tool names must be unique because model tool calls select tools by name. When you send `preset` and `tools` together, the explicit `tools` array replaces the preset's default tools. Hybrid apps that add local function tools should resolve the preset defaults first, merge in their local tools, then pass the merged array. The SDK rejects duplicate tool names before submitting requests.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { resolvePresetTools } from "@agent-api/sdk";
|
|
115
|
+
import { createLocalWorkspaceToolRegistry, LocalWorkspace } from "@agent-api/sdk/local";
|
|
116
|
+
|
|
117
|
+
const workspace = new LocalWorkspace("/path/to/project", { trusted: true });
|
|
118
|
+
const registry = createLocalWorkspaceToolRegistry(workspace);
|
|
119
|
+
|
|
120
|
+
const { tools } = await resolvePresetTools(client, {
|
|
121
|
+
preset: "pro-search",
|
|
122
|
+
tools: registry.definitions(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const response = await client.agent.create({
|
|
126
|
+
preset: "pro-search",
|
|
127
|
+
input: "Research the topic and update local notes.",
|
|
128
|
+
tools,
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For long-running apps, cache `client.presets.list()` and `client.tools.list()` and refresh them periodically. Use `resolvePresetToolsFromCatalog()` with cached catalogs when you want deterministic request construction without fetching on every turn.
|
|
133
|
+
|
|
100
134
|
## Skills
|
|
101
135
|
|
|
102
136
|
`localSkillFromDirectory()` reads `SKILL.md` into the descriptor for initial local-skill auto-focus; later focused reads still use the local skill tool bridge.
|
|
103
137
|
|
|
104
138
|
```typescript
|
|
105
|
-
import { localSkillFromDirectory } from "@agent-api/sdk";
|
|
139
|
+
import { localSkillFromDirectory } from "@agent-api/sdk/node";
|
|
106
140
|
|
|
107
141
|
const skill = await client.skills.create({ name: "research-helper" });
|
|
108
142
|
await client.skills.writeFile(skill.skill_id, "SKILL.md", "# Research helper\n");
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HTTPClient } from "./internal/http.js";
|
|
1
2
|
import { AuthResource } from "./resources/auth.js";
|
|
2
3
|
import { ModelsResource } from "./resources/models.js";
|
|
3
4
|
import { PresetsResource } from "./resources/presets.js";
|
|
@@ -25,8 +26,9 @@ export declare class AgentAPI {
|
|
|
25
26
|
readonly volumes: VolumesResource;
|
|
26
27
|
readonly skills: SkillsResource;
|
|
27
28
|
readonly auth: AuthResource;
|
|
28
|
-
|
|
29
|
+
protected readonly http: HTTPClient;
|
|
29
30
|
constructor(options?: ClientOptions);
|
|
31
|
+
protected createSkillsResource(): SkillsResource;
|
|
30
32
|
startDeviceAuth: (...args: Parameters<AuthResource["startDeviceAuth"]>) => Promise<import("./index.js").DeviceAuthStart>;
|
|
31
33
|
pollDeviceAuth: (...args: Parameters<AuthResource["pollDeviceAuth"]>) => Promise<import("./index.js").DeviceAuthPollResult>;
|
|
32
34
|
waitForDeviceAuth: (...args: Parameters<AuthResource["waitForDeviceAuth"]>) => Promise<import("./index.js").ApprovedDeviceAuth>;
|
package/dist/client.js
CHANGED
|
@@ -54,9 +54,12 @@ export class AgentAPI {
|
|
|
54
54
|
this.presets = new PresetsResource(this.http);
|
|
55
55
|
this.tools = new ToolsResource(this.http);
|
|
56
56
|
this.volumes = new VolumesResource(this.http);
|
|
57
|
-
this.skills =
|
|
57
|
+
this.skills = this.createSkillsResource();
|
|
58
58
|
this.auth = new AuthResource(this.http);
|
|
59
59
|
}
|
|
60
|
+
createSkillsResource() {
|
|
61
|
+
return new SkillsResource(this.http);
|
|
62
|
+
}
|
|
60
63
|
startDeviceAuth = (...args) => this.auth.startDeviceAuth(...args);
|
|
61
64
|
pollDeviceAuth = (...args) => this.auth.pollDeviceAuth(...args);
|
|
62
65
|
waitForDeviceAuth = (...args) => this.auth.waitForDeviceAuth(...args);
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,6 @@ export { APIError, APIConnectionError, APIStatusError, AuthenticationError, BadR
|
|
|
5
5
|
export * from "./types/index.js";
|
|
6
6
|
export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers, } from "./local-functions.js";
|
|
7
7
|
export type { LocalFunctionHandler, LocalFunctionHandlers } from "./local-functions.js";
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
10
|
-
export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
|
|
11
|
-
export type { LocalSkillDirectoryOptions } from "./local-skills.js";
|
|
8
|
+
export { mergeTools, publicToolToRequestTool, resolvePresetTools, resolvePresetToolsFromCatalog, } from "./preset-tools.js";
|
|
9
|
+
export type { PresetToolCatalogClient, ResolvePresetToolsOptions, ResolvePresetToolsResult, UnknownPresetToolBehavior, } from "./preset-tools.js";
|
|
12
10
|
export { AuthResource, DeviceAuthFlowError } from "./resources/auth.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,5 @@ export { Page, collectPage } from "./pagination.js";
|
|
|
3
3
|
export { APIError, APIConnectionError, APIStatusError, AuthenticationError, BadRequestError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, isRetryableStatus, parseResponseError, } from "./errors.js";
|
|
4
4
|
export * from "./types/index.js";
|
|
5
5
|
export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers, } from "./local-functions.js";
|
|
6
|
-
export {
|
|
7
|
-
export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
|
|
6
|
+
export { mergeTools, publicToolToRequestTool, resolvePresetTools, resolvePresetToolsFromCatalog, } from "./preset-tools.js";
|
|
8
7
|
export { AuthResource, DeviceAuthFlowError } from "./resources/auth.js";
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./index.js";
|
|
2
|
+
export { NodeAgentAPI } from "./node-client.js";
|
|
3
|
+
export { NodeSkillsResource } from "./resources/skills-node.js";
|
|
4
|
+
export { LocalWorkspaceDriver, createLocalWorkspaceToolRegistry, localWorkspaceToolDefinition, localWorkspaceToolInstructions, } from "./local/tools.js";
|
|
5
|
+
export type { LocalWorkspaceAction, LocalWorkspaceAccessMode, LocalWorkspaceToolRegistry, LocalWorkspaceToolRegistryOptions, } from "./local/tools.js";
|
|
6
|
+
export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
|
|
7
|
+
export type { LocalSkillDirectoryOptions } from "./local-skills.js";
|
|
8
|
+
export * from "./local/index.js";
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./index.js";
|
|
2
|
+
export { NodeAgentAPI } from "./node-client.js";
|
|
3
|
+
export { NodeSkillsResource } from "./resources/skills-node.js";
|
|
4
|
+
export { LocalWorkspaceDriver, createLocalWorkspaceToolRegistry, localWorkspaceToolDefinition, localWorkspaceToolInstructions, } from "./local/tools.js";
|
|
5
|
+
export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
|
|
6
|
+
export * from "./local/index.js";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ListPresetsResponse, Preset } from "./types/catalog.js";
|
|
2
|
+
import type { ListToolsResponse, PublicTool, Tool } from "./types/tools.js";
|
|
3
|
+
export interface PresetToolCatalogClient {
|
|
4
|
+
presets: {
|
|
5
|
+
list(): Promise<ListPresetsResponse>;
|
|
6
|
+
};
|
|
7
|
+
tools: {
|
|
8
|
+
list(): Promise<ListToolsResponse>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export type UnknownPresetToolBehavior = "stub" | "omit" | "error";
|
|
12
|
+
export interface ResolvePresetToolsOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Preset id whose policy.allowed_tools should be resolved.
|
|
15
|
+
*/
|
|
16
|
+
preset: string;
|
|
17
|
+
/**
|
|
18
|
+
* Additional caller/client tools to append after the preset tools.
|
|
19
|
+
*/
|
|
20
|
+
tools?: readonly Tool[];
|
|
21
|
+
/**
|
|
22
|
+
* Optional pre-fetched preset catalog. Use this when your app caches discovery
|
|
23
|
+
* responses and wants deterministic request construction.
|
|
24
|
+
*/
|
|
25
|
+
presets?: readonly Preset[];
|
|
26
|
+
/**
|
|
27
|
+
* Optional pre-fetched tool catalog.
|
|
28
|
+
*/
|
|
29
|
+
toolCatalog?: readonly PublicTool[];
|
|
30
|
+
/**
|
|
31
|
+
* Behavior when a preset allows a tool that is not present in the supplied
|
|
32
|
+
* or fetched tool catalog. "stub" keeps a name-only tool definition so the
|
|
33
|
+
* backend can still enrich it by name.
|
|
34
|
+
*/
|
|
35
|
+
unknownPresetTool?: UnknownPresetToolBehavior;
|
|
36
|
+
}
|
|
37
|
+
export interface ResolvePresetToolsResult {
|
|
38
|
+
preset: Preset;
|
|
39
|
+
tools: Tool[];
|
|
40
|
+
missingToolNames: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a preset's allowed tool names into concrete request tools and append
|
|
44
|
+
* caller-provided tools. This is a convenience for hybrid apps that need to add
|
|
45
|
+
* local/client tools while preserving the preset's default server tools.
|
|
46
|
+
*
|
|
47
|
+
* The Agent API request surface remains OpenAI-compatible: the returned array
|
|
48
|
+
* is intended for the normal CreateResponseRequest.tools field.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolvePresetTools(client: PresetToolCatalogClient, options: ResolvePresetToolsOptions): Promise<ResolvePresetToolsResult>;
|
|
51
|
+
export declare function resolvePresetToolsFromCatalog(options: ResolvePresetToolsOptions): ResolvePresetToolsResult;
|
|
52
|
+
export declare function mergeTools(...groups: Array<readonly Tool[]>): Tool[];
|
|
53
|
+
export declare function publicToolToRequestTool(tool: PublicTool): Tool;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { validateUniqueToolNames } from "./tool-validation.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a preset's allowed tool names into concrete request tools and append
|
|
4
|
+
* caller-provided tools. This is a convenience for hybrid apps that need to add
|
|
5
|
+
* local/client tools while preserving the preset's default server tools.
|
|
6
|
+
*
|
|
7
|
+
* The Agent API request surface remains OpenAI-compatible: the returned array
|
|
8
|
+
* is intended for the normal CreateResponseRequest.tools field.
|
|
9
|
+
*/
|
|
10
|
+
export async function resolvePresetTools(client, options) {
|
|
11
|
+
const presets = options.presets ?? (await client.presets.list()).data;
|
|
12
|
+
const toolCatalog = options.toolCatalog ?? (await client.tools.list()).data;
|
|
13
|
+
return resolvePresetToolsFromCatalog({ ...options, presets, toolCatalog });
|
|
14
|
+
}
|
|
15
|
+
export function resolvePresetToolsFromCatalog(options) {
|
|
16
|
+
const presetId = options.preset.trim();
|
|
17
|
+
if (!presetId) {
|
|
18
|
+
throw new Error("preset is required");
|
|
19
|
+
}
|
|
20
|
+
const preset = options.presets?.find((row) => row.preset === presetId);
|
|
21
|
+
if (!preset) {
|
|
22
|
+
throw new Error(`preset not found: ${presetId}`);
|
|
23
|
+
}
|
|
24
|
+
const catalogByName = new Map();
|
|
25
|
+
for (const tool of options.toolCatalog ?? []) {
|
|
26
|
+
const name = tool.name?.trim();
|
|
27
|
+
if (name)
|
|
28
|
+
catalogByName.set(name, tool);
|
|
29
|
+
}
|
|
30
|
+
const missingToolNames = [];
|
|
31
|
+
const presetTools = [];
|
|
32
|
+
const unknownPresetTool = options.unknownPresetTool ?? "stub";
|
|
33
|
+
for (const name of preset.policy?.allowed_tools ?? []) {
|
|
34
|
+
const trimmed = name.trim();
|
|
35
|
+
if (!trimmed)
|
|
36
|
+
continue;
|
|
37
|
+
const catalogTool = catalogByName.get(trimmed);
|
|
38
|
+
if (catalogTool) {
|
|
39
|
+
presetTools.push(publicToolToRequestTool(catalogTool));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
missingToolNames.push(trimmed);
|
|
43
|
+
if (unknownPresetTool === "error") {
|
|
44
|
+
throw new Error(`preset tool not found in catalog: ${trimmed}`);
|
|
45
|
+
}
|
|
46
|
+
if (unknownPresetTool === "stub") {
|
|
47
|
+
presetTools.push({ name: trimmed });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
preset,
|
|
52
|
+
tools: mergeTools(presetTools, options.tools ?? []),
|
|
53
|
+
missingToolNames,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function mergeTools(...groups) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const group of groups) {
|
|
59
|
+
for (const tool of group) {
|
|
60
|
+
const name = tool.name?.trim();
|
|
61
|
+
if (!name) {
|
|
62
|
+
throw new Error("tools[].name is required");
|
|
63
|
+
}
|
|
64
|
+
out.push({ ...tool, name });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
validateUniqueToolNames(out);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
export function publicToolToRequestTool(tool) {
|
|
71
|
+
const out = { name: tool.name };
|
|
72
|
+
if (tool.type)
|
|
73
|
+
out.type = tool.type;
|
|
74
|
+
if (tool.description)
|
|
75
|
+
out.description = tool.description;
|
|
76
|
+
if (tool.parameters)
|
|
77
|
+
out.parameters = tool.parameters;
|
|
78
|
+
if (tool.max_tokens != null)
|
|
79
|
+
out.max_tokens = tool.max_tokens;
|
|
80
|
+
if (tool.max_tokens_per_page != null)
|
|
81
|
+
out.max_tokens_per_page = tool.max_tokens_per_page;
|
|
82
|
+
if (tool.version)
|
|
83
|
+
out.version = tool.version;
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addOutputText } from "../internal/output-text.js";
|
|
2
2
|
import { buildQuery } from "../internal/query.js";
|
|
3
3
|
import { collectPage } from "../pagination.js";
|
|
4
|
+
import { validateUniqueToolNames } from "../tool-validation.js";
|
|
4
5
|
export class ResponsesResource {
|
|
5
6
|
http;
|
|
6
7
|
path;
|
|
@@ -9,6 +10,7 @@ export class ResponsesResource {
|
|
|
9
10
|
this.path = path;
|
|
10
11
|
}
|
|
11
12
|
create(params, options) {
|
|
13
|
+
validateUniqueToolNames(params.tools);
|
|
12
14
|
if (params.stream) {
|
|
13
15
|
return this.http.stream(this.path, params, options);
|
|
14
16
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SkillsResource } from "./skills.js";
|
|
2
|
+
import type { RequestOptions } from "../types/common.js";
|
|
3
|
+
import type { PullSkillDirectoryParams, PushSkillDirectoryParams, SkillDirectoryPullResult, SkillImportResponse } from "../types/skills.js";
|
|
4
|
+
export declare class NodeSkillsResource extends SkillsResource {
|
|
5
|
+
pushDirectory(skillID: string, rootDir: string, params?: PushSkillDirectoryParams, options?: RequestOptions): Promise<SkillImportResponse>;
|
|
6
|
+
pullDirectory(skillID: string, targetDir: string, params?: PullSkillDirectoryParams, options?: RequestOptions): Promise<SkillDirectoryPullResult>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { SkillsResource } from "./skills.js";
|
|
2
|
+
export class NodeSkillsResource extends SkillsResource {
|
|
3
|
+
async pushDirectory(skillID, rootDir, params = {}, options) {
|
|
4
|
+
const archive = await zipDirectory(rootDir);
|
|
5
|
+
return this.importArchive(skillID, archive, params, options);
|
|
6
|
+
}
|
|
7
|
+
async pullDirectory(skillID, targetDir, params = {}, options) {
|
|
8
|
+
const archive = await this.exportArchive(skillID, params, options);
|
|
9
|
+
return extractStoredZipToDirectory(archive.content, targetDir, { replace: params.replace === true });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async function zipDirectory(rootDir) {
|
|
13
|
+
const fs = await import("node:fs/promises");
|
|
14
|
+
const path = await import("node:path");
|
|
15
|
+
const root = path.resolve(rootDir);
|
|
16
|
+
const files = await walkArchiveFiles(fs, path, root, root);
|
|
17
|
+
const entries = [];
|
|
18
|
+
for (const rel of files.sort()) {
|
|
19
|
+
const content = await fs.readFile(path.join(root, rel));
|
|
20
|
+
entries.push({ path: rel, data: new Uint8Array(content.buffer, content.byteOffset, content.byteLength) });
|
|
21
|
+
}
|
|
22
|
+
return createStoredZip(entries);
|
|
23
|
+
}
|
|
24
|
+
async function walkArchiveFiles(fs, path, root, dir) {
|
|
25
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
if (entry.name === ".git" || entry.name === "__pycache__" || entry.name === "node_modules") {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const full = path.join(dir, entry.name);
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
out.push(...await walkArchiveFiles(fs, path, root, full));
|
|
34
|
+
}
|
|
35
|
+
else if (entry.isFile()) {
|
|
36
|
+
out.push(path.relative(root, full).split(path.sep).join("/"));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
async function extractStoredZipToDirectory(archive, targetDir, opts) {
|
|
42
|
+
const fs = await import("node:fs/promises");
|
|
43
|
+
const path = await import("node:path");
|
|
44
|
+
const root = path.resolve(targetDir);
|
|
45
|
+
if (opts.replace) {
|
|
46
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
await fs.mkdir(root, { recursive: true });
|
|
49
|
+
let fileCount = 0;
|
|
50
|
+
let byteCount = 0;
|
|
51
|
+
for (const entry of parseStoredZip(archive)) {
|
|
52
|
+
const dest = path.resolve(root, entry.path);
|
|
53
|
+
const rel = path.relative(root, dest);
|
|
54
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
55
|
+
throw new Error(`archive entry escapes target directory: ${entry.path}`);
|
|
56
|
+
}
|
|
57
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
58
|
+
await fs.writeFile(dest, entry.data);
|
|
59
|
+
fileCount += 1;
|
|
60
|
+
byteCount += entry.data.byteLength;
|
|
61
|
+
}
|
|
62
|
+
return { path: root, file_count: fileCount, byte_count: byteCount };
|
|
63
|
+
}
|
|
64
|
+
function createStoredZip(entries) {
|
|
65
|
+
const encoder = new TextEncoder();
|
|
66
|
+
const localParts = [];
|
|
67
|
+
const centralParts = [];
|
|
68
|
+
let offset = 0;
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const name = encoder.encode(entry.path);
|
|
71
|
+
const crc = crc32(entry.data);
|
|
72
|
+
const local = new Uint8Array(30 + name.length);
|
|
73
|
+
const lv = new DataView(local.buffer);
|
|
74
|
+
lv.setUint32(0, 0x04034b50, true);
|
|
75
|
+
lv.setUint16(4, 20, true);
|
|
76
|
+
lv.setUint16(8, 0, true);
|
|
77
|
+
lv.setUint32(14, crc, true);
|
|
78
|
+
lv.setUint32(18, entry.data.byteLength, true);
|
|
79
|
+
lv.setUint32(22, entry.data.byteLength, true);
|
|
80
|
+
lv.setUint16(26, name.length, true);
|
|
81
|
+
local.set(name, 30);
|
|
82
|
+
localParts.push(local, entry.data);
|
|
83
|
+
const central = new Uint8Array(46 + name.length);
|
|
84
|
+
const cv = new DataView(central.buffer);
|
|
85
|
+
cv.setUint32(0, 0x02014b50, true);
|
|
86
|
+
cv.setUint16(4, 20, true);
|
|
87
|
+
cv.setUint16(6, 20, true);
|
|
88
|
+
cv.setUint32(16, crc, true);
|
|
89
|
+
cv.setUint32(20, entry.data.byteLength, true);
|
|
90
|
+
cv.setUint32(24, entry.data.byteLength, true);
|
|
91
|
+
cv.setUint16(28, name.length, true);
|
|
92
|
+
cv.setUint32(42, offset, true);
|
|
93
|
+
central.set(name, 46);
|
|
94
|
+
centralParts.push(central);
|
|
95
|
+
offset += local.byteLength + entry.data.byteLength;
|
|
96
|
+
}
|
|
97
|
+
const centralOffset = offset;
|
|
98
|
+
const centralSize = centralParts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
99
|
+
const end = new Uint8Array(22);
|
|
100
|
+
const ev = new DataView(end.buffer);
|
|
101
|
+
ev.setUint32(0, 0x06054b50, true);
|
|
102
|
+
ev.setUint16(8, entries.length, true);
|
|
103
|
+
ev.setUint16(10, entries.length, true);
|
|
104
|
+
ev.setUint32(12, centralSize, true);
|
|
105
|
+
ev.setUint32(16, centralOffset, true);
|
|
106
|
+
const zip = concatUint8([...localParts, ...centralParts, end]);
|
|
107
|
+
return zip.buffer.slice(zip.byteOffset, zip.byteOffset + zip.byteLength);
|
|
108
|
+
}
|
|
109
|
+
function parseStoredZip(archive) {
|
|
110
|
+
const data = new Uint8Array(archive);
|
|
111
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
112
|
+
const entries = [];
|
|
113
|
+
let pos = 0;
|
|
114
|
+
const decoder = new TextDecoder();
|
|
115
|
+
while (pos + 4 <= data.byteLength && view.getUint32(pos, true) === 0x04034b50) {
|
|
116
|
+
const method = view.getUint16(pos + 8, true);
|
|
117
|
+
if (method !== 0) {
|
|
118
|
+
throw new Error("Only stored ZIP entries are supported by pullDirectory");
|
|
119
|
+
}
|
|
120
|
+
const compressedSize = view.getUint32(pos + 18, true);
|
|
121
|
+
const fileNameLength = view.getUint16(pos + 26, true);
|
|
122
|
+
const extraLength = view.getUint16(pos + 28, true);
|
|
123
|
+
const nameStart = pos + 30;
|
|
124
|
+
const contentStart = nameStart + fileNameLength + extraLength;
|
|
125
|
+
const contentEnd = contentStart + compressedSize;
|
|
126
|
+
if (contentEnd > data.byteLength) {
|
|
127
|
+
throw new Error("Invalid ZIP archive");
|
|
128
|
+
}
|
|
129
|
+
const name = decoder.decode(data.slice(nameStart, nameStart + fileNameLength)).replace(/\\/g, "/");
|
|
130
|
+
if (name && !name.endsWith("/") && !name.startsWith("__MACOSX/") && !name.split("/").includes("..")) {
|
|
131
|
+
entries.push({ path: name.replace(/^\/+/, ""), data: data.slice(contentStart, contentEnd) });
|
|
132
|
+
}
|
|
133
|
+
pos = contentEnd;
|
|
134
|
+
}
|
|
135
|
+
return entries;
|
|
136
|
+
}
|
|
137
|
+
function concatUint8(parts) {
|
|
138
|
+
const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
139
|
+
const out = new Uint8Array(total);
|
|
140
|
+
let offset = 0;
|
|
141
|
+
for (const part of parts) {
|
|
142
|
+
out.set(part, offset);
|
|
143
|
+
offset += part.byteLength;
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
function crc32(data) {
|
|
148
|
+
let crc = 0xffffffff;
|
|
149
|
+
for (const byte of data) {
|
|
150
|
+
crc ^= byte;
|
|
151
|
+
for (let i = 0; i < 8; i += 1) {
|
|
152
|
+
crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
156
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HTTPClient } from "../internal/http.js";
|
|
2
2
|
import type { RequestOptions } from "../types/common.js";
|
|
3
|
-
import type { CreateSkillParams, CreateSkillDevParams, CreateSkillDevResponse, DiscoverSkillsParams, FocusSkillParams, ImportSkillArchiveParams, ListSkillFilesParams, ListSkillFilesResponse, ListSkillsParams, ListSkillsResponse, ListSkillSummariesResponse,
|
|
3
|
+
import type { CreateSkillParams, CreateSkillDevParams, CreateSkillDevResponse, DiscoverSkillsParams, FocusSkillParams, ImportSkillArchiveParams, ListSkillFilesParams, ListSkillFilesResponse, ListSkillsParams, ListSkillsResponse, ListSkillSummariesResponse, Skill, SkillAcceptStrategy, SkillArchive, SkillArchiveParams, SkillBranchDiff, SkillBranchDiffParams, SkillFile, SkillBranch, SkillFocusResponse, SkillImportResponse, UpdateSkillFilePrimitiveParams, UpdateSkillFilePrimitiveResponse, UpdateSkillParams } from "../types/skills.js";
|
|
4
4
|
export declare class SkillsResource {
|
|
5
5
|
private readonly http;
|
|
6
6
|
constructor(http: HTTPClient);
|
|
@@ -35,6 +35,4 @@ export declare class SkillsResource {
|
|
|
35
35
|
exportArchive(skillID: string, params?: SkillArchiveParams, options?: RequestOptions): Promise<SkillArchive>;
|
|
36
36
|
importArchive(skillID: string, archive: ArrayBuffer | Blob, params?: ImportSkillArchiveParams, options?: RequestOptions): Promise<SkillImportResponse>;
|
|
37
37
|
diff(skillID: string, params?: SkillBranchDiffParams, options?: RequestOptions): Promise<SkillBranchDiff>;
|
|
38
|
-
pushDirectory(skillID: string, rootDir: string, params?: PushSkillDirectoryParams, options?: RequestOptions): Promise<SkillImportResponse>;
|
|
39
|
-
pullDirectory(skillID: string, targetDir: string, params?: PullSkillDirectoryParams, options?: RequestOptions): Promise<SkillDirectoryPullResult>;
|
|
40
38
|
}
|
package/dist/resources/skills.js
CHANGED
|
@@ -95,14 +95,6 @@ export class SkillsResource {
|
|
|
95
95
|
include_unchanged: params.include_unchanged ? "true" : undefined,
|
|
96
96
|
})}`, undefined, options);
|
|
97
97
|
}
|
|
98
|
-
async pushDirectory(skillID, rootDir, params = {}, options) {
|
|
99
|
-
const archive = await zipDirectory(rootDir);
|
|
100
|
-
return this.importArchive(skillID, archive, params, options);
|
|
101
|
-
}
|
|
102
|
-
async pullDirectory(skillID, targetDir, params = {}, options) {
|
|
103
|
-
const archive = await this.exportArchive(skillID, params, options);
|
|
104
|
-
return extractStoredZipToDirectory(archive.content, targetDir, { replace: params.replace === true });
|
|
105
|
-
}
|
|
106
98
|
}
|
|
107
99
|
function skillPath(path) {
|
|
108
100
|
return path
|
|
@@ -114,148 +106,3 @@ function skillPath(path) {
|
|
|
114
106
|
function normalizeArchivePath(path) {
|
|
115
107
|
return (path ?? "").trim().replace(/^\/+|\/+$/g, "");
|
|
116
108
|
}
|
|
117
|
-
async function zipDirectory(rootDir) {
|
|
118
|
-
const fs = await import("node:fs/promises");
|
|
119
|
-
const path = await import("node:path");
|
|
120
|
-
const root = path.resolve(rootDir);
|
|
121
|
-
const files = await walkArchiveFiles(fs, path, root, root);
|
|
122
|
-
const entries = [];
|
|
123
|
-
for (const rel of files.sort()) {
|
|
124
|
-
const content = await fs.readFile(path.join(root, rel));
|
|
125
|
-
entries.push({ path: rel, data: new Uint8Array(content.buffer, content.byteOffset, content.byteLength) });
|
|
126
|
-
}
|
|
127
|
-
return createStoredZip(entries);
|
|
128
|
-
}
|
|
129
|
-
async function walkArchiveFiles(fs, path, root, dir) {
|
|
130
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
131
|
-
const out = [];
|
|
132
|
-
for (const entry of entries) {
|
|
133
|
-
if (entry.name === ".git" || entry.name === "__pycache__" || entry.name === "node_modules") {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const full = path.join(dir, entry.name);
|
|
137
|
-
if (entry.isDirectory()) {
|
|
138
|
-
out.push(...await walkArchiveFiles(fs, path, root, full));
|
|
139
|
-
}
|
|
140
|
-
else if (entry.isFile()) {
|
|
141
|
-
out.push(path.relative(root, full).split(path.sep).join("/"));
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return out;
|
|
145
|
-
}
|
|
146
|
-
async function extractStoredZipToDirectory(archive, targetDir, opts) {
|
|
147
|
-
const fs = await import("node:fs/promises");
|
|
148
|
-
const path = await import("node:path");
|
|
149
|
-
const root = path.resolve(targetDir);
|
|
150
|
-
if (opts.replace) {
|
|
151
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
await fs.mkdir(root, { recursive: true });
|
|
154
|
-
let fileCount = 0;
|
|
155
|
-
let byteCount = 0;
|
|
156
|
-
for (const entry of parseStoredZip(archive)) {
|
|
157
|
-
const dest = path.resolve(root, entry.path);
|
|
158
|
-
const rel = path.relative(root, dest);
|
|
159
|
-
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
160
|
-
throw new Error(`archive entry escapes target directory: ${entry.path}`);
|
|
161
|
-
}
|
|
162
|
-
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
163
|
-
await fs.writeFile(dest, entry.data);
|
|
164
|
-
fileCount += 1;
|
|
165
|
-
byteCount += entry.data.byteLength;
|
|
166
|
-
}
|
|
167
|
-
return { path: root, file_count: fileCount, byte_count: byteCount };
|
|
168
|
-
}
|
|
169
|
-
function createStoredZip(entries) {
|
|
170
|
-
const encoder = new TextEncoder();
|
|
171
|
-
const localParts = [];
|
|
172
|
-
const centralParts = [];
|
|
173
|
-
let offset = 0;
|
|
174
|
-
for (const entry of entries) {
|
|
175
|
-
const name = encoder.encode(entry.path);
|
|
176
|
-
const crc = crc32(entry.data);
|
|
177
|
-
const local = new Uint8Array(30 + name.length);
|
|
178
|
-
const lv = new DataView(local.buffer);
|
|
179
|
-
lv.setUint32(0, 0x04034b50, true);
|
|
180
|
-
lv.setUint16(4, 20, true);
|
|
181
|
-
lv.setUint16(8, 0, true);
|
|
182
|
-
lv.setUint32(14, crc, true);
|
|
183
|
-
lv.setUint32(18, entry.data.byteLength, true);
|
|
184
|
-
lv.setUint32(22, entry.data.byteLength, true);
|
|
185
|
-
lv.setUint16(26, name.length, true);
|
|
186
|
-
local.set(name, 30);
|
|
187
|
-
localParts.push(local, entry.data);
|
|
188
|
-
const central = new Uint8Array(46 + name.length);
|
|
189
|
-
const cv = new DataView(central.buffer);
|
|
190
|
-
cv.setUint32(0, 0x02014b50, true);
|
|
191
|
-
cv.setUint16(4, 20, true);
|
|
192
|
-
cv.setUint16(6, 20, true);
|
|
193
|
-
cv.setUint32(16, crc, true);
|
|
194
|
-
cv.setUint32(20, entry.data.byteLength, true);
|
|
195
|
-
cv.setUint32(24, entry.data.byteLength, true);
|
|
196
|
-
cv.setUint16(28, name.length, true);
|
|
197
|
-
cv.setUint32(42, offset, true);
|
|
198
|
-
central.set(name, 46);
|
|
199
|
-
centralParts.push(central);
|
|
200
|
-
offset += local.byteLength + entry.data.byteLength;
|
|
201
|
-
}
|
|
202
|
-
const centralOffset = offset;
|
|
203
|
-
const centralSize = centralParts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
204
|
-
const end = new Uint8Array(22);
|
|
205
|
-
const ev = new DataView(end.buffer);
|
|
206
|
-
ev.setUint32(0, 0x06054b50, true);
|
|
207
|
-
ev.setUint16(8, entries.length, true);
|
|
208
|
-
ev.setUint16(10, entries.length, true);
|
|
209
|
-
ev.setUint32(12, centralSize, true);
|
|
210
|
-
ev.setUint32(16, centralOffset, true);
|
|
211
|
-
const zip = concatUint8([...localParts, ...centralParts, end]);
|
|
212
|
-
return zip.buffer.slice(zip.byteOffset, zip.byteOffset + zip.byteLength);
|
|
213
|
-
}
|
|
214
|
-
function parseStoredZip(archive) {
|
|
215
|
-
const data = new Uint8Array(archive);
|
|
216
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
217
|
-
const entries = [];
|
|
218
|
-
let pos = 0;
|
|
219
|
-
const decoder = new TextDecoder();
|
|
220
|
-
while (pos + 4 <= data.byteLength && view.getUint32(pos, true) === 0x04034b50) {
|
|
221
|
-
const method = view.getUint16(pos + 8, true);
|
|
222
|
-
if (method !== 0) {
|
|
223
|
-
throw new Error("Only stored ZIP entries are supported by pullDirectory");
|
|
224
|
-
}
|
|
225
|
-
const compressedSize = view.getUint32(pos + 18, true);
|
|
226
|
-
const fileNameLength = view.getUint16(pos + 26, true);
|
|
227
|
-
const extraLength = view.getUint16(pos + 28, true);
|
|
228
|
-
const nameStart = pos + 30;
|
|
229
|
-
const contentStart = nameStart + fileNameLength + extraLength;
|
|
230
|
-
const contentEnd = contentStart + compressedSize;
|
|
231
|
-
if (contentEnd > data.byteLength) {
|
|
232
|
-
throw new Error("Invalid ZIP archive");
|
|
233
|
-
}
|
|
234
|
-
const name = decoder.decode(data.slice(nameStart, nameStart + fileNameLength)).replace(/\\/g, "/");
|
|
235
|
-
if (name && !name.endsWith("/") && !name.startsWith("__MACOSX/") && !name.split("/").includes("..")) {
|
|
236
|
-
entries.push({ path: name.replace(/^\/+/, ""), data: data.slice(contentStart, contentEnd) });
|
|
237
|
-
}
|
|
238
|
-
pos = contentEnd;
|
|
239
|
-
}
|
|
240
|
-
return entries;
|
|
241
|
-
}
|
|
242
|
-
function concatUint8(parts) {
|
|
243
|
-
const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
244
|
-
const out = new Uint8Array(total);
|
|
245
|
-
let offset = 0;
|
|
246
|
-
for (const part of parts) {
|
|
247
|
-
out.set(part, offset);
|
|
248
|
-
offset += part.byteLength;
|
|
249
|
-
}
|
|
250
|
-
return out;
|
|
251
|
-
}
|
|
252
|
-
function crc32(data) {
|
|
253
|
-
let crc = 0xffffffff;
|
|
254
|
-
for (const byte of data) {
|
|
255
|
-
crc ^= byte;
|
|
256
|
-
for (let i = 0; i < 8; i += 1) {
|
|
257
|
-
crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return (crc ^ 0xffffffff) >>> 0;
|
|
261
|
-
}
|