openclaw-weiyuan-init 1.0.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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "action": "task.list",
3
+ "identity": ".weiyuan.dev2",
4
+ "projectId": "prj_demo"
5
+ }
6
+
@@ -0,0 +1,8 @@
1
+ {
2
+ "action": "risk.report",
3
+ "identity": ".weiyuan.dev2",
4
+ "projectId": "prj_demo",
5
+ "severity": "warn",
6
+ "summary": "3号库信号不稳"
7
+ }
8
+
@@ -0,0 +1,6 @@
1
+ {
2
+ "text": "我能做什么",
3
+ "identity": ".weiyuan.dev2",
4
+ "projectId": "prj_demo"
5
+ }
6
+
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "weiyuan-openclaw-skill",
3
+ "displayName": "微元协作 Skill",
4
+ "version": "0.1.1",
5
+ "description": "通过结构化 action 或自然语言 text 调用微元云服务。",
6
+ "entry": {
7
+ "type": "command",
8
+ "command": "npm run weiyuan:skill"
9
+ },
10
+ "ioSchema": "./schema.json",
11
+ "capabilities": {
12
+ "modes": [
13
+ "action",
14
+ "text"
15
+ ],
16
+ "actions": [
17
+ "init",
18
+ "create",
19
+ "join",
20
+ "task.list",
21
+ "task.take",
22
+ "task.submit",
23
+ "status",
24
+ "risk.report",
25
+ "capsule.search"
26
+ ]
27
+ },
28
+ "examples": [
29
+ "./examples/action-task-list.json",
30
+ "./examples/text-what-can-i-do.json",
31
+ "./examples/risk-report.json"
32
+ ],
33
+ "output": {
34
+ "success": {
35
+ "ok": true,
36
+ "data": {}
37
+ },
38
+ "error": {
39
+ "ok": false,
40
+ "errorCode": "UNKNOWN_ACTION",
41
+ "message": "",
42
+ "rawError": ""
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,104 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "weiyuan-skill-schema-v1",
4
+ "title": "Weiyuan OpenClaw Skill IO Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "input": {
8
+ "oneOf": [
9
+ {
10
+ "title": "ActionInput",
11
+ "type": "object",
12
+ "required": ["action"],
13
+ "properties": {
14
+ "action": {
15
+ "type": "string",
16
+ "enum": [
17
+ "init",
18
+ "create",
19
+ "join",
20
+ "task.list",
21
+ "task.take",
22
+ "task.submit",
23
+ "status",
24
+ "risk.report",
25
+ "capsule.search"
26
+ ]
27
+ },
28
+ "identity": { "type": "string" },
29
+ "projectId": { "type": "string" },
30
+ "taskId": { "type": "string" },
31
+ "code": { "type": "string" },
32
+ "role": { "type": "string", "enum": ["founder", "member", "guest"] },
33
+ "summary": { "type": "string" },
34
+ "severity": { "type": "string", "enum": ["info", "warn", "urgent"] },
35
+ "capsuleId": { "type": "string" },
36
+ "query": { "type": "string" },
37
+ "hash": { "type": "string" },
38
+ "title": { "type": "string" }
39
+ },
40
+ "additionalProperties": false
41
+ },
42
+ {
43
+ "title": "TextInput",
44
+ "type": "object",
45
+ "required": ["text"],
46
+ "properties": {
47
+ "text": { "type": "string", "minLength": 1 },
48
+ "identity": { "type": "string" },
49
+ "projectId": { "type": "string" },
50
+ "taskId": { "type": "string" },
51
+ "summary": { "type": "string" },
52
+ "severity": { "type": "string", "enum": ["info", "warn", "urgent"] },
53
+ "hash": { "type": "string" },
54
+ "query": { "type": "string" }
55
+ },
56
+ "additionalProperties": false
57
+ }
58
+ ]
59
+ },
60
+ "output": {
61
+ "oneOf": [
62
+ {
63
+ "title": "SuccessOutput",
64
+ "type": "object",
65
+ "required": ["ok"],
66
+ "properties": {
67
+ "ok": { "const": true },
68
+ "data": {}
69
+ },
70
+ "additionalProperties": true
71
+ },
72
+ {
73
+ "title": "ErrorOutput",
74
+ "type": "object",
75
+ "required": ["ok", "errorCode", "message"],
76
+ "properties": {
77
+ "ok": { "const": false },
78
+ "errorCode": {
79
+ "type": "string",
80
+ "enum": [
81
+ "MISSING_INPUT",
82
+ "INVALID_JSON",
83
+ "UNKNOWN_ACTION",
84
+ "CANNOT_MAP_TEXT",
85
+ "MISSING_PROJECT",
86
+ "MISSING_TASK",
87
+ "MISSING_CODE",
88
+ "MISSING_SUMMARY",
89
+ "CLI_ERROR",
90
+ "UNKNOWN"
91
+ ]
92
+ },
93
+ "message": { "type": "string" },
94
+ "rawError": { "type": "string" }
95
+ },
96
+ "additionalProperties": true
97
+ }
98
+ ]
99
+ }
100
+ },
101
+ "required": ["input", "output"],
102
+ "additionalProperties": false
103
+ }
104
+
@@ -0,0 +1,453 @@
1
+ import * as fs from "node:fs/promises"
2
+ import { DEFAULT_IDENTITY_PATH, readIdentity, writeIdentity } from "./weiyuanFile"
3
+ import { sha256Hex } from "./crypto"
4
+ import { signedRequest } from "./http"
5
+ import { bodyHashBase64, canonicalRequest, newKeyPair, signEd25519Base64 } from "./signing"
6
+
7
+ type Argv = { _: string[]; flags: Record<string, string | boolean> }
8
+
9
+ function parseArgv(argv: string[]): Argv {
10
+ const out: Argv = { _: [], flags: {} }
11
+ for (let i = 0; i < argv.length; i++) {
12
+ const a = argv[i]!
13
+ if (a.startsWith("--")) {
14
+ const key = a.slice(2)
15
+ const next = argv[i + 1]
16
+ if (next && !next.startsWith("-")) {
17
+ out.flags[key] = next
18
+ i++
19
+ } else {
20
+ out.flags[key] = true
21
+ }
22
+ } else {
23
+ out._.push(a)
24
+ }
25
+ }
26
+ return out
27
+ }
28
+
29
+ function mustString(flags: Record<string, string | boolean>, key: string): string {
30
+ const v = flags[key]
31
+ if (typeof v !== "string" || !v) throw new Error(`missing_${key}`)
32
+ return v
33
+ }
34
+
35
+ function optString(flags: Record<string, string | boolean>, key: string): string | undefined {
36
+ const v = flags[key]
37
+ return typeof v === "string" ? v : undefined
38
+ }
39
+
40
+ function print(obj: unknown): void {
41
+ process.stdout.write(`${JSON.stringify(obj, null, 2)}\n`)
42
+ }
43
+
44
+ function sleep(ms: number): Promise<void> {
45
+ return new Promise((resolve) => setTimeout(resolve, ms))
46
+ }
47
+
48
+ async function cmdInit(args: Argv): Promise<void> {
49
+ const serverBaseUrl = optString(args.flags, "server") ?? "http://127.0.0.1:8787"
50
+ const filePath = optString(args.flags, "out") ?? DEFAULT_IDENTITY_PATH
51
+ const kp = newKeyPair()
52
+ const identityHash = sha256Hex(JSON.stringify({ lobsterId: kp.lobsterId, publicKeyBase64: kp.publicKeyBase64 }))
53
+
54
+ const body = { lobsterId: kp.lobsterId, publicKeyBase64: kp.publicKeyBase64, identityHash }
55
+ const timestampMs = String(Date.now())
56
+ const nonce = `nonce_${Math.random().toString(16).slice(2)}`
57
+ const bodySha = bodyHashBase64(body)
58
+ const canonical = canonicalRequest({
59
+ method: "POST",
60
+ pathWithQuery: "/v1/init",
61
+ timestampMs,
62
+ nonce,
63
+ bodySha256Base64: bodySha,
64
+ })
65
+ const signature = signEd25519Base64(canonical, kp.secretKeyBase64)
66
+
67
+ const res = await fetch(`${serverBaseUrl}/v1/init`, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ "X-Weiyuan-Lobster-Id": kp.lobsterId,
72
+ "X-Weiyuan-Timestamp": timestampMs,
73
+ "X-Weiyuan-Nonce": nonce,
74
+ "X-Weiyuan-Signature": signature,
75
+ },
76
+ body: JSON.stringify(body),
77
+ })
78
+ const json = await res.json().catch(() => ({}))
79
+ if (res.status !== 200) throw new Error(JSON.stringify(json))
80
+
81
+ await writeIdentity(filePath, {
82
+ version: 1,
83
+ serverBaseUrl,
84
+ lobsterId: kp.lobsterId,
85
+ publicKeyBase64: kp.publicKeyBase64,
86
+ secretKeyBase64: kp.secretKeyBase64,
87
+ identityHash,
88
+ projectCursors: {},
89
+ })
90
+
91
+ print({ ok: true, lobsterId: kp.lobsterId, file: filePath, welcome: json.data?.welcome })
92
+ }
93
+
94
+ async function cmdCreateProject(args: Argv): Promise<void> {
95
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
96
+ const identity = await readIdentity(filePath)
97
+ const name = mustString(args.flags, "name")
98
+ const goal = optString(args.flags, "goal") ?? ""
99
+ const dna = mustString(args.flags, "dna")
100
+ const dnaHash = sha256Hex(dna)
101
+
102
+ const { status, json } = await signedRequest({
103
+ serverBaseUrl: identity.serverBaseUrl,
104
+ method: "POST",
105
+ pathWithQuery: "/v1/projects",
106
+ lobsterId: identity.lobsterId,
107
+ secretKeyBase64: identity.secretKeyBase64,
108
+ body: { name, goal, dnaHash },
109
+ })
110
+ if (status !== 200) throw new Error(JSON.stringify(json))
111
+ print({ ok: true, projectId: json.data.projectId })
112
+ }
113
+
114
+ async function cmdInvite(args: Argv): Promise<void> {
115
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
116
+ const identity = await readIdentity(filePath)
117
+ const projectId = mustString(args.flags, "project")
118
+ const role = mustString(args.flags, "role")
119
+ const { status, json } = await signedRequest({
120
+ serverBaseUrl: identity.serverBaseUrl,
121
+ method: "POST",
122
+ pathWithQuery: "/v1/invites",
123
+ lobsterId: identity.lobsterId,
124
+ secretKeyBase64: identity.secretKeyBase64,
125
+ body: { projectId, role },
126
+ })
127
+ if (status !== 200) throw new Error(JSON.stringify(json))
128
+ print({ ok: true, code: json.data.code })
129
+ }
130
+
131
+ async function cmdJoin(args: Argv): Promise<void> {
132
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
133
+ const identity = await readIdentity(filePath)
134
+ const projectId = mustString(args.flags, "project")
135
+ const code = mustString(args.flags, "code")
136
+ const { status, json } = await signedRequest({
137
+ serverBaseUrl: identity.serverBaseUrl,
138
+ method: "POST",
139
+ pathWithQuery: `/v1/projects/${projectId}/join`,
140
+ lobsterId: identity.lobsterId,
141
+ secretKeyBase64: identity.secretKeyBase64,
142
+ body: { code },
143
+ })
144
+ if (status !== 200) throw new Error(JSON.stringify(json))
145
+ print({ ok: true, welcome: json.data?.welcome })
146
+ }
147
+
148
+ async function cmdTaskList(args: Argv): Promise<void> {
149
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
150
+ const identity = await readIdentity(filePath)
151
+ const projectId = mustString(args.flags, "project")
152
+ const { status, json } = await signedRequest({
153
+ serverBaseUrl: identity.serverBaseUrl,
154
+ method: "GET",
155
+ pathWithQuery: `/v1/projects/${projectId}/tasks`,
156
+ lobsterId: identity.lobsterId,
157
+ secretKeyBase64: identity.secretKeyBase64,
158
+ })
159
+ if (status !== 200) throw new Error(JSON.stringify(json))
160
+ print(json.data)
161
+ }
162
+
163
+ async function cmdTaskCreate(args: Argv): Promise<void> {
164
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
165
+ const identity = await readIdentity(filePath)
166
+ const projectId = mustString(args.flags, "project")
167
+ const title = mustString(args.flags, "title")
168
+ const desc = optString(args.flags, "desc")
169
+ const { status, json } = await signedRequest({
170
+ serverBaseUrl: identity.serverBaseUrl,
171
+ method: "POST",
172
+ pathWithQuery: `/v1/projects/${projectId}/tasks`,
173
+ lobsterId: identity.lobsterId,
174
+ secretKeyBase64: identity.secretKeyBase64,
175
+ body: { title, desc },
176
+ })
177
+ if (status !== 200) throw new Error(JSON.stringify(json))
178
+ print({ ok: true, taskId: json.data.taskId })
179
+ }
180
+
181
+ async function cmdTaskTake(args: Argv): Promise<void> {
182
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
183
+ const identity = await readIdentity(filePath)
184
+ const projectId = mustString(args.flags, "project")
185
+ const taskId = mustString(args.flags, "task")
186
+ const { status, json } = await signedRequest({
187
+ serverBaseUrl: identity.serverBaseUrl,
188
+ method: "POST",
189
+ pathWithQuery: `/v1/projects/${projectId}/tasks/${taskId}/take`,
190
+ lobsterId: identity.lobsterId,
191
+ secretKeyBase64: identity.secretKeyBase64,
192
+ body: {},
193
+ })
194
+ if (status !== 200) throw new Error(JSON.stringify(json))
195
+ print({ ok: true })
196
+ }
197
+
198
+ async function cmdTaskAbandon(args: Argv): Promise<void> {
199
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
200
+ const identity = await readIdentity(filePath)
201
+ const projectId = mustString(args.flags, "project")
202
+ const taskId = mustString(args.flags, "task")
203
+ const { status, json } = await signedRequest({
204
+ serverBaseUrl: identity.serverBaseUrl,
205
+ method: "POST",
206
+ pathWithQuery: `/v1/projects/${projectId}/tasks/${taskId}/abandon`,
207
+ lobsterId: identity.lobsterId,
208
+ secretKeyBase64: identity.secretKeyBase64,
209
+ body: {},
210
+ })
211
+ if (status !== 200) throw new Error(JSON.stringify(json))
212
+ print({ ok: true })
213
+ }
214
+
215
+ async function cmdTaskSubmit(args: Argv): Promise<void> {
216
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
217
+ const identity = await readIdentity(filePath)
218
+ const projectId = mustString(args.flags, "project")
219
+ const taskId = mustString(args.flags, "task")
220
+ const contentHash = mustString(args.flags, "hash")
221
+ const contentType = optString(args.flags, "type") ?? "text/markdown"
222
+ const { status, json } = await signedRequest({
223
+ serverBaseUrl: identity.serverBaseUrl,
224
+ method: "POST",
225
+ pathWithQuery: `/v1/projects/${projectId}/tasks/${taskId}/submit`,
226
+ lobsterId: identity.lobsterId,
227
+ secretKeyBase64: identity.secretKeyBase64,
228
+ body: { artifacts: [{ contentHash, contentType }] },
229
+ })
230
+ if (status !== 200) throw new Error(JSON.stringify(json))
231
+ print({ ok: true })
232
+ }
233
+
234
+ async function cmdStatus(args: Argv): Promise<void> {
235
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
236
+ const identity = await readIdentity(filePath)
237
+ const projectId = mustString(args.flags, "project")
238
+ const { status, json } = await signedRequest({
239
+ serverBaseUrl: identity.serverBaseUrl,
240
+ method: "GET",
241
+ pathWithQuery: `/v1/projects/${projectId}/status`,
242
+ lobsterId: identity.lobsterId,
243
+ secretKeyBase64: identity.secretKeyBase64,
244
+ })
245
+ if (status !== 200) throw new Error(JSON.stringify(json))
246
+ print(json.data)
247
+ }
248
+
249
+ async function cmdMemberList(args: Argv): Promise<void> {
250
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
251
+ const identity = await readIdentity(filePath)
252
+ const projectId = mustString(args.flags, "project")
253
+ const { status, json } = await signedRequest({
254
+ serverBaseUrl: identity.serverBaseUrl,
255
+ method: "GET",
256
+ pathWithQuery: `/v1/projects/${projectId}/members`,
257
+ lobsterId: identity.lobsterId,
258
+ secretKeyBase64: identity.secretKeyBase64,
259
+ })
260
+ if (status !== 200) throw new Error(JSON.stringify(json))
261
+ print(json.data)
262
+ }
263
+
264
+ async function cmdSync(args: Argv): Promise<void> {
265
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
266
+ const identity = await readIdentity(filePath)
267
+ const projectId = mustString(args.flags, "project")
268
+ const follow = Boolean(args.flags.follow)
269
+ const intervalMs = Number(optString(args.flags, "interval") ?? 2000)
270
+
271
+ const runOnce = async (): Promise<void> => {
272
+ const cursor = identity.projectCursors[projectId] ?? 0
273
+ const { status, json } = await signedRequest({
274
+ serverBaseUrl: identity.serverBaseUrl,
275
+ method: "GET",
276
+ pathWithQuery: `/v1/projects/${projectId}/events?cursor=${cursor}`,
277
+ lobsterId: identity.lobsterId,
278
+ secretKeyBase64: identity.secretKeyBase64,
279
+ })
280
+ if (status !== 200) throw new Error(JSON.stringify(json))
281
+ identity.projectCursors[projectId] = Number(json.data.cursor ?? cursor)
282
+ await writeIdentity(filePath, identity)
283
+ print({ ok: true, cursor: identity.projectCursors[projectId], events: json.data.events })
284
+ }
285
+
286
+ await runOnce()
287
+ if (!follow) return
288
+ while (true) {
289
+ await sleep(intervalMs)
290
+ await runOnce()
291
+ }
292
+ }
293
+
294
+ async function cmdRiskReport(args: Argv): Promise<void> {
295
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
296
+ const identity = await readIdentity(filePath)
297
+ const projectId = mustString(args.flags, "project")
298
+ const severity = mustString(args.flags, "severity")
299
+ const summary = mustString(args.flags, "summary")
300
+ const taskId = optString(args.flags, "task")
301
+ const { status, json } = await signedRequest({
302
+ serverBaseUrl: identity.serverBaseUrl,
303
+ method: "POST",
304
+ pathWithQuery: `/v1/projects/${projectId}/risks`,
305
+ lobsterId: identity.lobsterId,
306
+ secretKeyBase64: identity.secretKeyBase64,
307
+ body: { severity, summary, taskId },
308
+ })
309
+ if (status !== 200) throw new Error(JSON.stringify(json))
310
+ print({ ok: true, riskId: json.data.riskId })
311
+ }
312
+
313
+ async function cmdRiskList(args: Argv): Promise<void> {
314
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
315
+ const identity = await readIdentity(filePath)
316
+ const projectId = mustString(args.flags, "project")
317
+ const { status, json } = await signedRequest({
318
+ serverBaseUrl: identity.serverBaseUrl,
319
+ method: "GET",
320
+ pathWithQuery: `/v1/projects/${projectId}/risks`,
321
+ lobsterId: identity.lobsterId,
322
+ secretKeyBase64: identity.secretKeyBase64,
323
+ })
324
+ if (status !== 200) throw new Error(JSON.stringify(json))
325
+ print(json.data)
326
+ }
327
+
328
+ async function cmdCapsulePublish(args: Argv): Promise<void> {
329
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
330
+ const identity = await readIdentity(filePath)
331
+ const name = mustString(args.flags, "name")
332
+ const kind = mustString(args.flags, "kind")
333
+ const version = optString(args.flags, "version") ?? "v1"
334
+ const packagePath = mustString(args.flags, "file")
335
+ const bytes = await fs.readFile(packagePath)
336
+ const packageHash = sha256Hex(bytes)
337
+ const manifest = {
338
+ name,
339
+ kind,
340
+ version,
341
+ dnaTags: optString(args.flags, "dnaTags")?.split(",").map((x) => x.trim()).filter(Boolean) ?? [],
342
+ projectTags: optString(args.flags, "projectTags")?.split(",").map((x) => x.trim()).filter(Boolean) ?? [],
343
+ }
344
+ const manifestHash = sha256Hex(JSON.stringify(manifest))
345
+ const packageBytesBase64 = Buffer.from(bytes).toString("base64")
346
+
347
+ const { status, json } = await signedRequest({
348
+ serverBaseUrl: identity.serverBaseUrl,
349
+ method: "POST",
350
+ pathWithQuery: "/v1/capsules",
351
+ lobsterId: identity.lobsterId,
352
+ secretKeyBase64: identity.secretKeyBase64,
353
+ body: { ...manifest, manifestHash, packageHash, packageBytesBase64 },
354
+ })
355
+ if (status !== 200) throw new Error(JSON.stringify(json))
356
+ print({ ok: true, capsuleId: json.data.capsuleId, manifestHash, packageHash })
357
+ }
358
+
359
+ async function cmdCapsuleSearch(args: Argv): Promise<void> {
360
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
361
+ const identity = await readIdentity(filePath)
362
+ const query = optString(args.flags, "query") ?? ""
363
+ const { status, json } = await signedRequest({
364
+ serverBaseUrl: identity.serverBaseUrl,
365
+ method: "GET",
366
+ pathWithQuery: `/v1/capsules?query=${encodeURIComponent(query)}`,
367
+ lobsterId: identity.lobsterId,
368
+ secretKeyBase64: identity.secretKeyBase64,
369
+ })
370
+ if (status !== 200) throw new Error(JSON.stringify(json))
371
+ print(json.data)
372
+ }
373
+
374
+ async function cmdCapsulePull(args: Argv): Promise<void> {
375
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
376
+ const identity = await readIdentity(filePath)
377
+ const capsuleId = mustString(args.flags, "id")
378
+ const out = optString(args.flags, "out")
379
+ const { status, json } = await signedRequest({
380
+ serverBaseUrl: identity.serverBaseUrl,
381
+ method: "POST",
382
+ pathWithQuery: `/v1/capsules/${capsuleId}/pull`,
383
+ lobsterId: identity.lobsterId,
384
+ secretKeyBase64: identity.secretKeyBase64,
385
+ body: {},
386
+ })
387
+ if (status !== 200) throw new Error(JSON.stringify(json))
388
+ const capsule = json.data.capsule
389
+ if (out && capsule?.packageBytesBase64) {
390
+ await fs.writeFile(out, Buffer.from(capsule.packageBytesBase64, "base64"))
391
+ }
392
+ print({ ok: true, capsule })
393
+ }
394
+
395
+ async function cmdCapsuleReport(args: Argv): Promise<void> {
396
+ const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
397
+ const identity = await readIdentity(filePath)
398
+ const capsuleId = mustString(args.flags, "id")
399
+ const success = (optString(args.flags, "success") ?? "true").toLowerCase() !== "false"
400
+ const { status, json } = await signedRequest({
401
+ serverBaseUrl: identity.serverBaseUrl,
402
+ method: "POST",
403
+ pathWithQuery: `/v1/capsules/${capsuleId}/report`,
404
+ lobsterId: identity.lobsterId,
405
+ secretKeyBase64: identity.secretKeyBase64,
406
+ body: { success },
407
+ })
408
+ if (status !== 200) throw new Error(JSON.stringify(json))
409
+ print({ ok: true, capsuleId })
410
+ }
411
+
412
+ export async function runCli(argv: string[]): Promise<void> {
413
+ const args = parseArgv(argv)
414
+ const [cmd, sub] = args._.length ? [args._[0], args._[1]] : [undefined, undefined]
415
+
416
+ if (!cmd) throw new Error("missing_command")
417
+
418
+ if (cmd === "init") return await cmdInit(args)
419
+ if (cmd === "create") return await cmdCreateProject(args)
420
+ if (cmd === "invite") return await cmdInvite(args)
421
+ if (cmd === "join") return await cmdJoin(args)
422
+ if (cmd === "status") return await cmdStatus(args)
423
+ if (cmd === "sync") return await cmdSync(args)
424
+ if (cmd === "member") {
425
+ if (sub === "list") return await cmdMemberList(args)
426
+ throw new Error("unknown_member_subcommand")
427
+ }
428
+
429
+ if (cmd === "risk") {
430
+ if (sub === "report") return await cmdRiskReport(args)
431
+ if (sub === "list") return await cmdRiskList(args)
432
+ throw new Error("unknown_risk_subcommand")
433
+ }
434
+
435
+ if (cmd === "capsule") {
436
+ if (sub === "publish") return await cmdCapsulePublish(args)
437
+ if (sub === "search") return await cmdCapsuleSearch(args)
438
+ if (sub === "pull") return await cmdCapsulePull(args)
439
+ if (sub === "report") return await cmdCapsuleReport(args)
440
+ throw new Error("unknown_capsule_subcommand")
441
+ }
442
+
443
+ if (cmd === "task") {
444
+ if (sub === "list") return await cmdTaskList(args)
445
+ if (sub === "create") return await cmdTaskCreate(args)
446
+ if (sub === "take") return await cmdTaskTake(args)
447
+ if (sub === "abandon") return await cmdTaskAbandon(args)
448
+ if (sub === "submit") return await cmdTaskSubmit(args)
449
+ throw new Error("unknown_task_subcommand")
450
+ }
451
+
452
+ throw new Error("unknown_command")
453
+ }
@@ -0,0 +1,7 @@
1
+ import { runCli } from "./cli"
2
+
3
+ runCli(process.argv.slice(2)).catch((e) => {
4
+ process.stderr.write(String(e instanceof Error ? e.stack ?? e.message : e))
5
+ process.exit(1)
6
+ })
7
+
@@ -0,0 +1,14 @@
1
+ import * as crypto from "node:crypto"
2
+
3
+ export function sha256Base64(input: string | Uint8Array): string {
4
+ const h = crypto.createHash("sha256")
5
+ h.update(input)
6
+ return h.digest("base64")
7
+ }
8
+
9
+ export function sha256Hex(input: string | Uint8Array): string {
10
+ const h = crypto.createHash("sha256")
11
+ h.update(input)
12
+ return h.digest("hex")
13
+ }
14
+