@falari/harness 0.0.0-stage → 0.8.9
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/CHANGELOG.md +444 -0
- package/LICENSE +21 -0
- package/README.md +212 -2
- package/client.js +3303 -0
- package/cordis.patch.yml +6 -0
- package/icon.png +0 -0
- package/index.js +52 -0
- package/lib/config.js +103 -0
- package/lib/errors.js +40 -0
- package/lib/falari-sdk.cjs +39775 -0
- package/lib/pin.js +126 -0
- package/lib/routes.js +311 -0
- package/lib/sdk.js +13 -0
- package/lib/service.js +1429 -0
- package/lib/tools.js +193 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +68 -4
package/lib/tools.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools. Every tool calls FalariDataService, which is the same object the
|
|
3
|
+
* Web routes use — one operation, two callers.
|
|
4
|
+
*/
|
|
5
|
+
import { ServiceError } from './errors.js';
|
|
6
|
+
|
|
7
|
+
/** Stop `wait: true` here so a tool call stays well inside the call timeout. */
|
|
8
|
+
const WAIT_LIMIT_MS = 120_000;
|
|
9
|
+
const WAIT_POLL_MS = 1500;
|
|
10
|
+
|
|
11
|
+
const TERMINAL = new Set(['completed', 'terminated', 'failed', 'needs_source']);
|
|
12
|
+
const ACTIVE = new Set(['creating', 'queued', 'uploading', 'paused']);
|
|
13
|
+
|
|
14
|
+
function text(value) {
|
|
15
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sleep(ms) {
|
|
19
|
+
return new Promise((done) => setTimeout(done, ms));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const output = {
|
|
23
|
+
schema: { type: 'object', additionalProperties: true },
|
|
24
|
+
render(_args, value) {
|
|
25
|
+
return text(value);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function registerTools(ctx, service) {
|
|
30
|
+
ctx.tools.register({
|
|
31
|
+
name: 'falari_status',
|
|
32
|
+
description:
|
|
33
|
+
'Show the Falari Agent Key status: configured state, agent address, permission tiers, allowance, balance, expiry and network. Call this before any Falari upload or download. When no key is configured, tell the user to open the plugin page: Plugins → Falari Data → Configure.',
|
|
34
|
+
parameters: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
refresh: { type: 'boolean', description: 'Read the chain instead of the cached status.' },
|
|
38
|
+
},
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
output,
|
|
42
|
+
execute: async (args) => service.keyStatus({ refresh: Boolean(args && args.refresh) }),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
ctx.tools.register({
|
|
46
|
+
name: 'falari_quote',
|
|
47
|
+
description:
|
|
48
|
+
'Price a Falari upload before spending anything. Give either `path` (a file inside the session working directory) or `size_bytes`. Returns the escrow deposit, service fees, the total, and whether the Agent Key allowance and owner balance cover it.',
|
|
49
|
+
parameters: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
path: { type: 'string', description: 'File path inside the session working directory.' },
|
|
53
|
+
size_bytes: { type: 'number', description: 'File size in bytes when there is no local file.' },
|
|
54
|
+
duration: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Storage duration: permanent, 30d, 90d, 1y, 5y, 10y, or a number of seconds.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
61
|
+
output,
|
|
62
|
+
execute: async (args, exec) => {
|
|
63
|
+
const input = args && typeof args === 'object' ? args : {};
|
|
64
|
+
let size = Number(input.size_bytes);
|
|
65
|
+
if (!Number.isFinite(size) || size <= 0) {
|
|
66
|
+
if (!input.path) {
|
|
67
|
+
throw new ServiceError('missing_size', 'Pass either path or size_bytes to price an upload.');
|
|
68
|
+
}
|
|
69
|
+
const target = await service.guardedSourcePath(exec.agent, input.path);
|
|
70
|
+
const info = await service.assertUploadableFile(target);
|
|
71
|
+
size = info.size;
|
|
72
|
+
}
|
|
73
|
+
return service.quote({ sizeBytes: size, duration: input.duration });
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
ctx.tools.register({
|
|
78
|
+
name: 'falari_upload',
|
|
79
|
+
description:
|
|
80
|
+
'Upload a local file to Falari storage with the configured Agent Key. The path must stay inside the session working directory. Uploading spends FAR: call falari_quote first when the user cares about cost. Returns a task; with wait=true it waits up to two minutes and otherwise reports the task so falari_task can be polled.',
|
|
81
|
+
parameters: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
path: { type: 'string', description: 'File path inside the session working directory.' },
|
|
85
|
+
privacy: { type: 'string', enum: ['public', 'private'], description: 'Encrypt client-side (private) or upload plaintext (public).' },
|
|
86
|
+
duration: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Storage duration: permanent, 30d, 90d, 1y, 5y, 10y, or a number of seconds.',
|
|
89
|
+
},
|
|
90
|
+
file_name: { type: 'string', description: 'Name recorded on chain; defaults to the file name.' },
|
|
91
|
+
wait: { type: 'boolean', description: 'Wait (up to 2 minutes) for the upload to finish before returning.' },
|
|
92
|
+
},
|
|
93
|
+
required: ['path'],
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
},
|
|
96
|
+
output,
|
|
97
|
+
execute: async (args, exec) => {
|
|
98
|
+
const input = args && typeof args === 'object' ? args : {};
|
|
99
|
+
const target = await service.guardedSourcePath(exec.agent, input.path);
|
|
100
|
+
let task = await service.enqueueUpload({
|
|
101
|
+
path: target,
|
|
102
|
+
privacy: input.privacy,
|
|
103
|
+
duration: input.duration,
|
|
104
|
+
fileName: input.file_name,
|
|
105
|
+
});
|
|
106
|
+
if (input.wait !== true) return task;
|
|
107
|
+
const deadline = Date.now() + WAIT_LIMIT_MS;
|
|
108
|
+
while (!TERMINAL.has(task.status) && Date.now() < deadline) {
|
|
109
|
+
await sleep(WAIT_POLL_MS);
|
|
110
|
+
try {
|
|
111
|
+
task = await service.getTask(task.id);
|
|
112
|
+
} catch {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { ...task, timedOut: !TERMINAL.has(task.status) };
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
ctx.tools.register({
|
|
121
|
+
name: 'falari_task',
|
|
122
|
+
description:
|
|
123
|
+
'Read Falari upload tasks: pass task_id for one task, or omit it for the recent list. Use this to follow an upload started with wait=false.',
|
|
124
|
+
parameters: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
task_id: { type: 'string', description: 'Task id returned by falari_upload.' },
|
|
128
|
+
active_only: { type: 'boolean', description: 'Only return tasks that are still running or paused.' },
|
|
129
|
+
},
|
|
130
|
+
additionalProperties: false,
|
|
131
|
+
},
|
|
132
|
+
output,
|
|
133
|
+
execute: async (args) => {
|
|
134
|
+
const input = args && typeof args === 'object' ? args : {};
|
|
135
|
+
if (input.task_id) return service.getTask(String(input.task_id));
|
|
136
|
+
const tasks = await service.listTasks();
|
|
137
|
+
return input.active_only === true ? tasks.filter((task) => ACTIVE.has(task.status)) : tasks;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
ctx.tools.register({
|
|
142
|
+
name: 'falari_list',
|
|
143
|
+
description:
|
|
144
|
+
"List this account's Falari intents (uploaded files and their deal state), newest first. Read-only: listing needs no key tier.",
|
|
145
|
+
parameters: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
limit: { type: 'number', description: 'How many intents to return (default 50, max 200).' },
|
|
149
|
+
},
|
|
150
|
+
additionalProperties: false,
|
|
151
|
+
},
|
|
152
|
+
output,
|
|
153
|
+
execute: async (args) => {
|
|
154
|
+
const input = args && typeof args === 'object' ? args : {};
|
|
155
|
+
return service.listIntents({ limit: Number(input.limit) || 50 });
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
ctx.tools.register({
|
|
160
|
+
name: 'falari_download',
|
|
161
|
+
description:
|
|
162
|
+
'Download one Falari intent to disk, verify its roots and decrypt it when private. Public data needs no key; private data needs the Data Key or an Agent Key whose data grant covers the intent. Waits up to two minutes, then returns the task so falari_task can be polled.',
|
|
163
|
+
parameters: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
intent_id: { type: 'string', description: 'Intent id, for example intent_<hex>.' },
|
|
167
|
+
data_key: { type: 'string', description: 'Base64 32-byte Data Key, needed when this key cannot unwrap the intent.' },
|
|
168
|
+
out_path: { type: 'string', description: 'File name or path inside the download directory.' },
|
|
169
|
+
},
|
|
170
|
+
required: ['intent_id'],
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
},
|
|
173
|
+
output,
|
|
174
|
+
execute: async (args) => {
|
|
175
|
+
const input = args && typeof args === 'object' ? args : {};
|
|
176
|
+
let task = await service.startDownload({
|
|
177
|
+
intentId: input.intent_id,
|
|
178
|
+
dataKey: input.data_key,
|
|
179
|
+
outPath: input.out_path,
|
|
180
|
+
});
|
|
181
|
+
const deadline = Date.now() + WAIT_LIMIT_MS;
|
|
182
|
+
while ((task.status === 'running' || task.status === 'queued') && Date.now() < deadline) {
|
|
183
|
+
await sleep(WAIT_POLL_MS);
|
|
184
|
+
try {
|
|
185
|
+
task = await service.getTask(task.id);
|
|
186
|
+
} catch {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { ...task, timedOut: task.status === 'running' || task.status === 'queued' };
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
}
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@falari/harness",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.8.9",
|
|
4
|
+
"description": "Falari Data client for DeepSeek Harness: Agent Key status, data upload and download.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Falari",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/falarichain/harness.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/falarichain/harness",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/falarichain/harness/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"falari",
|
|
17
|
+
"harness",
|
|
18
|
+
"dsh",
|
|
19
|
+
"storage",
|
|
20
|
+
"agent-key"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./index.js",
|
|
28
|
+
"./client": "./client.js",
|
|
29
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
"./locale/*.json": "./locale/*.json",
|
|
32
|
+
"./icon": "./icon.png"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"index.js",
|
|
36
|
+
"client.js",
|
|
37
|
+
"lib/*.js",
|
|
38
|
+
"lib/falari-sdk.cjs",
|
|
39
|
+
"cordis.patch.yml",
|
|
40
|
+
"locale/*.json",
|
|
41
|
+
"icon.png",
|
|
42
|
+
"README.md",
|
|
43
|
+
"CHANGELOG.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build:sdk": "bash scripts/build-sdk.sh",
|
|
48
|
+
"test:unit": "node scripts/unit-host.mjs && node scripts/unit-pin.mjs && node scripts/unit-vault.mjs",
|
|
49
|
+
"test:host": "node scripts/smoke-host.mjs",
|
|
50
|
+
"test:client": "node scripts/smoke-client.mjs",
|
|
51
|
+
"test": "node scripts/unit-host.mjs && node scripts/unit-pin.mjs && node scripts/unit-vault.mjs && node scripts/smoke-host.mjs && node scripts/smoke-client.mjs"
|
|
52
|
+
},
|
|
53
|
+
"dsh": {
|
|
54
|
+
"bundle": {
|
|
55
|
+
"patch": "./cordis.patch.yml"
|
|
56
|
+
},
|
|
57
|
+
"client": {
|
|
58
|
+
"platform": "web",
|
|
59
|
+
"immediately": true,
|
|
60
|
+
"inject": [
|
|
61
|
+
"@deepseek-ai/dsh-client-locale",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public",
|
|
68
|
+
"registry": "https://registry.npmjs.org"
|
|
69
|
+
}
|
|
70
|
+
}
|