@ixo/flow-work 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.
- package/README.md +49 -0
- package/dist/capability.d.ts +26 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +4 -0
- package/dist/capability.js.map +1 -0
- package/dist/flow-work.plugin.d.ts +24 -0
- package/dist/flow-work.plugin.d.ts.map +1 -0
- package/dist/flow-work.plugin.js +92 -0
- package/dist/flow-work.plugin.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/listener.d.ts +51 -0
- package/dist/listener.d.ts.map +1 -0
- package/dist/listener.js +226 -0
- package/dist/listener.js.map +1 -0
- package/dist/matrix-port.d.ts +34 -0
- package/dist/matrix-port.d.ts.map +1 -0
- package/dist/matrix-port.js +63 -0
- package/dist/matrix-port.js.map +1 -0
- package/dist/protocol.d.ts +32 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +23 -0
- package/dist/protocol.js.map +1 -0
- package/dist/ucan.d.ts +34 -0
- package/dist/ucan.d.ts.map +1 -0
- package/dist/ucan.js +43 -0
- package/dist/ucan.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @ixo/flow-work
|
|
2
|
+
|
|
3
|
+
Oracle-side flow-work plugin: accept UCAN-delegated `ixo.flow.work` invocations
|
|
4
|
+
from the IXO flow-manager over Matrix and post receipts.
|
|
5
|
+
|
|
6
|
+
It is the oracle half of the flow-work contract. Flow-manager discovers a
|
|
7
|
+
capable oracle, mints a UCAN delegation scoped to a flow node, and sends an
|
|
8
|
+
`ixo.flow.work.invoke` event carrying the work brief by value. `FlowWorkListener`
|
|
9
|
+
validates the delegation (audience, expiry, signature chain, nb caveats),
|
|
10
|
+
runs the matching capability handler, and posts an `ixo.flow.work.receipt`.
|
|
11
|
+
The oracle never gains read authority over anything — inputs arrive in the
|
|
12
|
+
invocation, the result leaves in the receipt.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add @ixo/flow-work
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Peer dependencies: `@ixo/oracle-runtime`, `@ixo/ucan`, `@nestjs/common`,
|
|
21
|
+
`matrix-js-sdk`.
|
|
22
|
+
|
|
23
|
+
## Releasing
|
|
24
|
+
|
|
25
|
+
This package versions independently of the flow-manager app via
|
|
26
|
+
[Changesets](https://github.com/changesets/changesets). It lives outside the
|
|
27
|
+
pnpm workspace with its own lockfile, so its Changesets setup is self-contained
|
|
28
|
+
in this directory.
|
|
29
|
+
|
|
30
|
+
1. Make your change, then record it:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
cd packages/flow-work
|
|
34
|
+
pnpm changeset # pick patch / minor / major, write a summary
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Commit the generated `.changeset/*.md` file with your PR.
|
|
38
|
+
|
|
39
|
+
2. On merge to `main`, the **Release @ixo/flow-work** workflow opens a
|
|
40
|
+
`release/flow-work` PR that bumps `package.json` + `CHANGELOG.md`.
|
|
41
|
+
|
|
42
|
+
3. Merging that PR publishes the new version to npm via OIDC trusted publishing.
|
|
43
|
+
|
|
44
|
+
A change with no changeset never releases this package, and a change to the
|
|
45
|
+
flow-manager app never releases this package — the two version independently.
|
|
46
|
+
|
|
47
|
+
> The initial `0.1.0` publish is a one-time manual bootstrap
|
|
48
|
+
> (`publish-flow-work.yml`, run from the Actions tab). Configure the npm trusted
|
|
49
|
+
> publisher after that first release; the Changesets workflow needs no token.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* A contractable capability: the unit of work this oracle accepts from
|
|
4
|
+
* flow-manager. `can` is what flow-manager discovers and delegates
|
|
5
|
+
* (`cap:<can>` keyword on the domain card); `actionType` narrows which
|
|
6
|
+
* handler runs; the handler is a plain async function — no coupling to the
|
|
7
|
+
* runtime's tool registry.
|
|
8
|
+
*/
|
|
9
|
+
export interface FlowWorkCapability<Schema extends z.ZodType = z.ZodType> {
|
|
10
|
+
/** Capability ability, e.g. 'flow/diagnose'. Must match the UCAN grant. */
|
|
11
|
+
can: string;
|
|
12
|
+
/** Work discriminator within the capability, e.g. 'diagnose_http_request'. */
|
|
13
|
+
actionType: string;
|
|
14
|
+
description: string;
|
|
15
|
+
/**
|
|
16
|
+
* Claim schema constraints published on the domain card — the structured
|
|
17
|
+
* list flow-manager filters on. An empty array means no schema restriction
|
|
18
|
+
* (matches any). Omit entirely for capabilities that don't touch claims.
|
|
19
|
+
*/
|
|
20
|
+
claimSchemas?: string[];
|
|
21
|
+
/** Validates the invocation's `inputs` before the handler runs. */
|
|
22
|
+
inputSchema: Schema;
|
|
23
|
+
handler: (inputs: z.infer<Schema>) => Promise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
export declare function findCapability(capabilities: readonly FlowWorkCapability[], can: string, actionType: string): FlowWorkCapability | undefined;
|
|
26
|
+
//# sourceMappingURL=capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IACtE,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxD;AAED,wBAAgB,cAAc,CAC5B,YAAY,EAAE,SAAS,kBAAkB,EAAE,EAC3C,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,kBAAkB,GAAG,SAAS,CAIhC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,cAAc,CAC5B,YAA2C,EAC3C,GAAW,EACX,UAAkB;IAElB,OAAO,YAAY,CAAC,IAAI,CACtB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,CAChE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { OraclePlugin, z, type PluginContext, type PluginManifest } from '@ixo/oracle-runtime';
|
|
2
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
3
|
+
import type * as sdk from 'matrix-js-sdk';
|
|
4
|
+
import type { FlowWorkCapability } from './capability.js';
|
|
5
|
+
export interface FlowWorkPluginOptions {
|
|
6
|
+
matrixClient: sdk.MatrixClient;
|
|
7
|
+
capabilities: FlowWorkCapability[];
|
|
8
|
+
}
|
|
9
|
+
export declare class FlowWorkPlugin extends OraclePlugin {
|
|
10
|
+
private readonly options;
|
|
11
|
+
readonly name = "flow-work";
|
|
12
|
+
readonly version = "0.1.0";
|
|
13
|
+
readonly manifest: PluginManifest;
|
|
14
|
+
readonly configSchema: z.ZodObject<{
|
|
15
|
+
FLOW_WORK_TRUSTED_HOMESERVERS: z.ZodString;
|
|
16
|
+
ORACLE_ENTITY_DID: z.ZodString;
|
|
17
|
+
BLOCKSYNC_GRAPHQL_URL: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
readonly autoDetectHint = "FLOW_WORK_TRUSTED_HOMESERVERS";
|
|
20
|
+
constructor(options: FlowWorkPluginOptions);
|
|
21
|
+
autoDetect(env: NodeJS.ProcessEnv): boolean;
|
|
22
|
+
getNestModules(ctx?: PluginContext): DynamicModule[];
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=flow-work.plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-work.plugin.d.ts","sourceRoot":"","sources":["../src/flow-work.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,CAAC,EACD,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,KAAK,GAAG,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AA4B1D,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC;IAC/B,YAAY,EAAE,kBAAkB,EAAE,CAAC;CACpC;AAED,qBAAa,cAAe,SAAQ,YAAY;IAWlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAVpC,QAAQ,CAAC,IAAI,eAAe;IAE5B,QAAQ,CAAC,OAAO,WAAW;IAE3B,QAAQ,CAAC,QAAQ,iBAAY;IAE7B,SAAkB,YAAY;;;;sBAAgB;IAE9C,SAAkB,cAAc,mCAAmC;gBAEtC,OAAO,EAAE,qBAAqB;IAIlD,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IAI3C,cAAc,CAAC,GAAG,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE;CAiD9D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { OraclePlugin, z, } from '@ixo/oracle-runtime';
|
|
8
|
+
import { Module, } from '@nestjs/common';
|
|
9
|
+
import { FlowWorkListener } from './listener.js';
|
|
10
|
+
import { adaptMatrixClient } from './matrix-port.js';
|
|
11
|
+
import { buildDelegationValidator } from './ucan.js';
|
|
12
|
+
const configSchema = z.object({
|
|
13
|
+
/** Comma-separated homeserver domains whose room invites are accepted. */
|
|
14
|
+
FLOW_WORK_TRUSTED_HOMESERVERS: z.string().trim().min(1),
|
|
15
|
+
/** This oracle's DID — the expected UCAN delegation audience. */
|
|
16
|
+
ORACLE_ENTITY_DID: z.string().trim().min(1),
|
|
17
|
+
/** Blocksync GraphQL endpoint for did:ixo key resolution. */
|
|
18
|
+
BLOCKSYNC_GRAPHQL_URL: z.string().url().optional(),
|
|
19
|
+
});
|
|
20
|
+
const manifest = {
|
|
21
|
+
title: 'Flow Work',
|
|
22
|
+
summary: 'Accepts contracted work from flow-manager: joins trusted rooms, validates UCAN-delegated ' +
|
|
23
|
+
'ixo.flow.work.invoke events, runs the declared capability handler, and posts receipts.',
|
|
24
|
+
whenToUse: [],
|
|
25
|
+
whenNotToUse: [],
|
|
26
|
+
examples: [],
|
|
27
|
+
tags: ['flow', 'delegation', 'ucan', 'matrix'],
|
|
28
|
+
category: 'automation',
|
|
29
|
+
visibility: 'silent',
|
|
30
|
+
stability: 'experimental',
|
|
31
|
+
};
|
|
32
|
+
export class FlowWorkPlugin extends OraclePlugin {
|
|
33
|
+
options;
|
|
34
|
+
name = 'flow-work';
|
|
35
|
+
version = '0.1.0';
|
|
36
|
+
manifest = manifest;
|
|
37
|
+
configSchema = configSchema;
|
|
38
|
+
autoDetectHint = 'FLOW_WORK_TRUSTED_HOMESERVERS';
|
|
39
|
+
constructor(options) {
|
|
40
|
+
super();
|
|
41
|
+
this.options = options;
|
|
42
|
+
}
|
|
43
|
+
autoDetect(env) {
|
|
44
|
+
return Boolean(env.FLOW_WORK_TRUSTED_HOMESERVERS?.trim());
|
|
45
|
+
}
|
|
46
|
+
getNestModules(ctx) {
|
|
47
|
+
if (!ctx)
|
|
48
|
+
throw new Error('FlowWorkPlugin requires a PluginContext');
|
|
49
|
+
const config = configSchema.parse(ctx.config);
|
|
50
|
+
const trustedHomeservers = config.FLOW_WORK_TRUSTED_HOMESERVERS.split(',')
|
|
51
|
+
.map((value) => value.trim())
|
|
52
|
+
.filter((value) => value.length > 0);
|
|
53
|
+
const logger = ctx.logger;
|
|
54
|
+
const listener = new FlowWorkListener({
|
|
55
|
+
port: adaptMatrixClient(this.options.matrixClient),
|
|
56
|
+
oracleDid: config.ORACLE_ENTITY_DID,
|
|
57
|
+
trustedHomeservers,
|
|
58
|
+
capabilities: this.options.capabilities,
|
|
59
|
+
validateDelegation: buildDelegationValidator({
|
|
60
|
+
oracleDid: config.ORACLE_ENTITY_DID,
|
|
61
|
+
blocksyncUrl: config.BLOCKSYNC_GRAPHQL_URL,
|
|
62
|
+
}),
|
|
63
|
+
logger,
|
|
64
|
+
});
|
|
65
|
+
const matrixClient = this.options.matrixClient;
|
|
66
|
+
class FlowWorkLifecycle {
|
|
67
|
+
onModuleInit() {
|
|
68
|
+
// The shared client is also used by EditorPlugin; only start it if
|
|
69
|
+
// nothing else has.
|
|
70
|
+
if (!matrixClient.clientRunning) {
|
|
71
|
+
void matrixClient.startClient();
|
|
72
|
+
}
|
|
73
|
+
listener.start();
|
|
74
|
+
}
|
|
75
|
+
onModuleDestroy() {
|
|
76
|
+
listener.stop();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
let FlowWorkModule = class FlowWorkModule {
|
|
80
|
+
};
|
|
81
|
+
FlowWorkModule = __decorate([
|
|
82
|
+
Module({ providers: [FlowWorkLifecycle] })
|
|
83
|
+
], FlowWorkModule);
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
module: FlowWorkModule,
|
|
87
|
+
providers: [FlowWorkLifecycle],
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=flow-work.plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-work.plugin.js","sourceRoot":"","sources":["../src/flow-work.plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { FlowWorkPlugin, type FlowWorkPluginOptions, } from './flow-work.plugin.js';
|
|
2
|
+
export { findCapability, type FlowWorkCapability } from './capability.js';
|
|
3
|
+
export { FlowWorkListener, homeserverOf } from './listener.js';
|
|
4
|
+
export { adaptMatrixClient, type FlowWorkMatrixPort } from './matrix-port.js';
|
|
5
|
+
export { FLOW_WORK_ASSIGN_EVENT, FLOW_WORK_INVOKE_EVENT, FLOW_WORK_RECEIPT_EVENT, flowWorkInvokeSchema, type FlowWorkErrorCode, type FlowWorkInvoke, type FlowWorkReceipt, } from './protocol.js';
|
|
6
|
+
export { buildDelegationValidator, type DelegationValidator } from './ucan.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { FlowWorkPlugin, } from './flow-work.plugin.js';
|
|
2
|
+
export { findCapability } from './capability.js';
|
|
3
|
+
export { FlowWorkListener, homeserverOf } from './listener.js';
|
|
4
|
+
export { adaptMatrixClient } from './matrix-port.js';
|
|
5
|
+
export { FLOW_WORK_ASSIGN_EVENT, FLOW_WORK_INVOKE_EVENT, FLOW_WORK_RECEIPT_EVENT, flowWorkInvokeSchema, } from './protocol.js';
|
|
6
|
+
export { buildDelegationValidator } from './ucan.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAA2B,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAA2B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,wBAAwB,EAA4B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type FlowWorkCapability } from './capability.js';
|
|
2
|
+
import type { FlowWorkMatrixPort } from './matrix-port.js';
|
|
3
|
+
import type { DelegationValidator } from './ucan.js';
|
|
4
|
+
export interface FlowWorkLogger {
|
|
5
|
+
log(message: string): void;
|
|
6
|
+
warn(message: string): void;
|
|
7
|
+
error(message: string): void;
|
|
8
|
+
}
|
|
9
|
+
export interface FlowWorkListenerOptions {
|
|
10
|
+
port: FlowWorkMatrixPort;
|
|
11
|
+
/** This oracle's DID — invocations addressed elsewhere are ignored. */
|
|
12
|
+
oracleDid: string;
|
|
13
|
+
/** Homeserver domains whose room invites are accepted. */
|
|
14
|
+
trustedHomeservers: readonly string[];
|
|
15
|
+
capabilities: readonly FlowWorkCapability[];
|
|
16
|
+
validateDelegation: DelegationValidator;
|
|
17
|
+
logger: FlowWorkLogger;
|
|
18
|
+
/** How many recent room events to scan for an existing receipt. */
|
|
19
|
+
receiptScanLimit?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Hostname of '@user:server.tld' or a bare/prefixed server name. */
|
|
22
|
+
export declare function homeserverOf(value: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* The oracle side of the flow-work contract: accepts invites from trusted
|
|
25
|
+
* homeservers, executes UCAN-authorized `ixo.flow.work.invoke` events via
|
|
26
|
+
* the declared capability handlers, and posts `ixo.flow.work.receipt`s.
|
|
27
|
+
*/
|
|
28
|
+
export declare class FlowWorkListener {
|
|
29
|
+
private readonly options;
|
|
30
|
+
private readonly trusted;
|
|
31
|
+
private readonly active;
|
|
32
|
+
private readonly completed;
|
|
33
|
+
private unsubscribe;
|
|
34
|
+
constructor(options: FlowWorkListenerOptions);
|
|
35
|
+
start(): void;
|
|
36
|
+
stop(): void;
|
|
37
|
+
private handleInvite;
|
|
38
|
+
private handleRoomEvent;
|
|
39
|
+
/**
|
|
40
|
+
* Bind nb caveats to the invocation before executing. A delegation minted
|
|
41
|
+
* for one claim schema must not be reusable with unrelated claim data:
|
|
42
|
+
* `nb.claimSchema` requires the invocation's `inputs.claimSchemaId` to be
|
|
43
|
+
* present and equal.
|
|
44
|
+
*/
|
|
45
|
+
private enforceCaveats;
|
|
46
|
+
private execute;
|
|
47
|
+
private hasExistingReceipt;
|
|
48
|
+
private postFailure;
|
|
49
|
+
private postReceipt;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,kBAAkB,CAAC;AAS1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,kBAAkB,CAAC;IACzB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,YAAY,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC5C,kBAAkB,EAAE,mBAAmB,CAAC;IACxC,MAAM,EAAE,cAAc,CAAC;IACvB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAYD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBzD;AAMD;;;;GAIG;AACH,qBAAa,gBAAgB;IAMf,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,WAAW,CAAyB;gBAEf,OAAO,EAAE,uBAAuB;IAa7D,KAAK,IAAI,IAAI;IAuBb,IAAI,IAAI,IAAI;YAKE,YAAY;YAcZ,eAAe;IAgE7B;;;;;OAKG;IACH,OAAO,CAAC,cAAc;YAiBR,OAAO;IAqDrB,OAAO,CAAC,kBAAkB;YAYZ,WAAW;YAcX,WAAW;CAQ1B"}
|
package/dist/listener.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { findCapability } from './capability.js';
|
|
2
|
+
import { FLOW_WORK_INVOKE_EVENT, FLOW_WORK_RECEIPT_EVENT, flowWorkInvokeSchema, } from './protocol.js';
|
|
3
|
+
class FlowWorkFailure extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = 'FlowWorkFailure';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Hostname of '@user:server.tld' or a bare/prefixed server name. */
|
|
12
|
+
export function homeserverOf(value) {
|
|
13
|
+
let candidate = value.trim().toLowerCase();
|
|
14
|
+
if (candidate.startsWith('@')) {
|
|
15
|
+
const separator = candidate.indexOf(':');
|
|
16
|
+
if (separator < 0)
|
|
17
|
+
return null;
|
|
18
|
+
candidate = candidate.slice(separator + 1);
|
|
19
|
+
}
|
|
20
|
+
if (candidate === '')
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return new URL(candidate.includes('://') ? candidate : `matrix://${candidate}`).hostname
|
|
24
|
+
.replace(/\.$/, '')
|
|
25
|
+
.toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isRecord(value) {
|
|
32
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The oracle side of the flow-work contract: accepts invites from trusted
|
|
36
|
+
* homeservers, executes UCAN-authorized `ixo.flow.work.invoke` events via
|
|
37
|
+
* the declared capability handlers, and posts `ixo.flow.work.receipt`s.
|
|
38
|
+
*/
|
|
39
|
+
export class FlowWorkListener {
|
|
40
|
+
options;
|
|
41
|
+
trusted;
|
|
42
|
+
active = new Set();
|
|
43
|
+
completed = new Set();
|
|
44
|
+
unsubscribe = [];
|
|
45
|
+
constructor(options) {
|
|
46
|
+
this.options = options;
|
|
47
|
+
const trusted = options.trustedHomeservers
|
|
48
|
+
.map(homeserverOf)
|
|
49
|
+
.filter((value) => value !== null);
|
|
50
|
+
this.trusted = new Set(trusted);
|
|
51
|
+
if (this.trusted.size === 0) {
|
|
52
|
+
throw new Error('flow-work has no valid trusted homeserver domains');
|
|
53
|
+
}
|
|
54
|
+
if (options.capabilities.length === 0) {
|
|
55
|
+
throw new Error('flow-work has no declared capabilities');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
start() {
|
|
59
|
+
const { port, logger } = this.options;
|
|
60
|
+
this.unsubscribe = [
|
|
61
|
+
port.onInvite((invite) => {
|
|
62
|
+
void this.handleInvite(invite).catch((error) => {
|
|
63
|
+
logger.error(`[flow-work] invite handling failed: ${message(error)}`);
|
|
64
|
+
});
|
|
65
|
+
}),
|
|
66
|
+
port.onRoomEvent((event) => {
|
|
67
|
+
void this.handleRoomEvent(event).catch((error) => {
|
|
68
|
+
logger.error(`[flow-work] event handling failed in ${event.roomId}: ${message(error)}`);
|
|
69
|
+
});
|
|
70
|
+
}),
|
|
71
|
+
];
|
|
72
|
+
logger.log(`[flow-work] listening — capabilities: ${this.options.capabilities
|
|
73
|
+
.map((entry) => `${entry.can}#${entry.actionType}`)
|
|
74
|
+
.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
stop() {
|
|
77
|
+
for (const off of this.unsubscribe)
|
|
78
|
+
off();
|
|
79
|
+
this.unsubscribe = [];
|
|
80
|
+
}
|
|
81
|
+
async handleInvite(invite) {
|
|
82
|
+
const host = homeserverOf(invite.inviterUserId);
|
|
83
|
+
if (!host || !this.trusted.has(host)) {
|
|
84
|
+
this.options.logger.warn(`[flow-work] rejected invite to ${invite.roomId} from untrusted sender ${invite.inviterUserId}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await this.options.port.joinRoom(invite.roomId);
|
|
88
|
+
this.options.logger.log(`[flow-work] joined ${invite.roomId} (invited by ${invite.inviterUserId})`);
|
|
89
|
+
}
|
|
90
|
+
async handleRoomEvent(event) {
|
|
91
|
+
if (event.type !== FLOW_WORK_INVOKE_EVENT || !isRecord(event.content)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (event.content.audience !== this.options.oracleDid)
|
|
95
|
+
return;
|
|
96
|
+
const rawId = event.content.invocationId;
|
|
97
|
+
if (typeof rawId !== 'string' || rawId.trim() === '')
|
|
98
|
+
return;
|
|
99
|
+
const invocationId = rawId.trim();
|
|
100
|
+
const key = `${event.roomId}:${invocationId}`;
|
|
101
|
+
if (this.active.has(key) || this.completed.has(key))
|
|
102
|
+
return;
|
|
103
|
+
this.active.add(key);
|
|
104
|
+
let receiptPosted = false;
|
|
105
|
+
try {
|
|
106
|
+
if (this.hasExistingReceipt(event.roomId, invocationId)) {
|
|
107
|
+
this.completed.add(key);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const parsed = flowWorkInvokeSchema.safeParse(event.content);
|
|
111
|
+
if (!parsed.success) {
|
|
112
|
+
await this.postFailure(event.roomId, invocationId, 'input_invalid', parsed.error.issues
|
|
113
|
+
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
114
|
+
.join('; '));
|
|
115
|
+
receiptPosted = true;
|
|
116
|
+
this.completed.add(key);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const receipt = await this.execute(parsed.data);
|
|
120
|
+
await this.postReceipt(event.roomId, receipt);
|
|
121
|
+
receiptPosted = true;
|
|
122
|
+
this.completed.add(key);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const failure = error instanceof FlowWorkFailure
|
|
126
|
+
? error
|
|
127
|
+
: new FlowWorkFailure('tool_error', message(error));
|
|
128
|
+
try {
|
|
129
|
+
await this.postFailure(event.roomId, invocationId, failure.code, failure.message);
|
|
130
|
+
receiptPosted = true;
|
|
131
|
+
this.completed.add(key);
|
|
132
|
+
}
|
|
133
|
+
catch (postError) {
|
|
134
|
+
this.options.logger.error(`[flow-work] failed to post receipt for ${invocationId}: ${message(postError)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.active.delete(key);
|
|
139
|
+
if (!receiptPosted)
|
|
140
|
+
this.completed.delete(key);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Bind nb caveats to the invocation before executing. A delegation minted
|
|
145
|
+
* for one claim schema must not be reusable with unrelated claim data:
|
|
146
|
+
* `nb.claimSchema` requires the invocation's `inputs.claimSchemaId` to be
|
|
147
|
+
* present and equal.
|
|
148
|
+
*/
|
|
149
|
+
enforceCaveats(nb, invoke) {
|
|
150
|
+
const claimSchema = nb?.claimSchema;
|
|
151
|
+
if (typeof claimSchema !== 'string' || claimSchema === '')
|
|
152
|
+
return;
|
|
153
|
+
const provided = invoke.inputs.claimSchemaId;
|
|
154
|
+
if (provided !== claimSchema) {
|
|
155
|
+
throw new FlowWorkFailure('delegation_invalid', `Delegation is caveated to claim schema '${claimSchema}' but the invocation carries '${typeof provided === 'string' ? provided : 'none'}'.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async execute(invoke) {
|
|
159
|
+
const capability = findCapability(this.options.capabilities, invoke.capability, invoke.actionType);
|
|
160
|
+
if (!capability) {
|
|
161
|
+
throw new FlowWorkFailure('capability_not_declared', `Capability '${invoke.capability}' with actionType '${invoke.actionType}' is not declared by this oracle.`);
|
|
162
|
+
}
|
|
163
|
+
const validation = await this.options.validateDelegation(invoke.delegation);
|
|
164
|
+
if (!validation.ok) {
|
|
165
|
+
throw new FlowWorkFailure(validation.code, validation.message);
|
|
166
|
+
}
|
|
167
|
+
if (validation.capability &&
|
|
168
|
+
validation.capability.can !== invoke.capability) {
|
|
169
|
+
throw new FlowWorkFailure('delegation_invalid', `Delegation grants '${validation.capability.can}', not '${invoke.capability}'.`);
|
|
170
|
+
}
|
|
171
|
+
this.enforceCaveats(validation.capability?.nb, invoke);
|
|
172
|
+
const inputs = capability.inputSchema.safeParse(invoke.inputs);
|
|
173
|
+
if (!inputs.success) {
|
|
174
|
+
throw new FlowWorkFailure('input_invalid', inputs.error.issues
|
|
175
|
+
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
176
|
+
.join('; '));
|
|
177
|
+
}
|
|
178
|
+
let output;
|
|
179
|
+
try {
|
|
180
|
+
output = await capability.handler(inputs.data);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new FlowWorkFailure('tool_error', message(error));
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
invocationId: invoke.invocationId,
|
|
187
|
+
status: 'success',
|
|
188
|
+
output: toReceiptOutput(output),
|
|
189
|
+
error: null,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
hasExistingReceipt(roomId, invocationId) {
|
|
193
|
+
const limit = this.options.receiptScanLimit ?? 100;
|
|
194
|
+
return this.options.port
|
|
195
|
+
.recentEvents(roomId, limit)
|
|
196
|
+
.some((event) => event.type === FLOW_WORK_RECEIPT_EVENT &&
|
|
197
|
+
isRecord(event.content) &&
|
|
198
|
+
event.content.invocationId === invocationId);
|
|
199
|
+
}
|
|
200
|
+
async postFailure(roomId, invocationId, code, errorMessage) {
|
|
201
|
+
await this.postReceipt(roomId, {
|
|
202
|
+
invocationId,
|
|
203
|
+
status: 'failure',
|
|
204
|
+
output: null,
|
|
205
|
+
error: { code, message: errorMessage },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async postReceipt(roomId, receipt) {
|
|
209
|
+
await this.options.port.sendEvent(roomId, FLOW_WORK_RECEIPT_EVENT, {
|
|
210
|
+
...receipt,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function toReceiptOutput(value) {
|
|
215
|
+
if (value === null || value === undefined)
|
|
216
|
+
return null;
|
|
217
|
+
const json = JSON.stringify(value);
|
|
218
|
+
if (json === undefined)
|
|
219
|
+
return { value: null };
|
|
220
|
+
const serializable = JSON.parse(json);
|
|
221
|
+
return isRecord(serializable) ? serializable : { value: serializable };
|
|
222
|
+
}
|
|
223
|
+
function message(error) {
|
|
224
|
+
return error instanceof Error ? error.message : String(error);
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=listener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener.js","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type * as sdk from 'matrix-js-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* The narrow slice of Matrix the flow-work listener needs. The listener
|
|
4
|
+
* depends on this port, not on matrix-js-sdk — production wraps the real
|
|
5
|
+
* client (adaptMatrixClient), tests provide an in-memory implementation.
|
|
6
|
+
*/
|
|
7
|
+
declare module 'matrix-js-sdk' {
|
|
8
|
+
interface TimelineEvents {
|
|
9
|
+
[key: `ixo.flow.work.${string}`]: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export interface FlowWorkRoomEvent {
|
|
13
|
+
roomId: string;
|
|
14
|
+
type: string;
|
|
15
|
+
content: unknown;
|
|
16
|
+
sender?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FlowWorkInvite {
|
|
19
|
+
roomId: string;
|
|
20
|
+
inviterUserId: string;
|
|
21
|
+
}
|
|
22
|
+
export interface FlowWorkMatrixPort {
|
|
23
|
+
/** Register an invite handler. Returns an unsubscribe function. */
|
|
24
|
+
onInvite(handler: (invite: FlowWorkInvite) => void): () => void;
|
|
25
|
+
/** Register a room-event handler (decrypted where applicable). Returns unsubscribe. */
|
|
26
|
+
onRoomEvent(handler: (event: FlowWorkRoomEvent) => void): () => void;
|
|
27
|
+
joinRoom(roomId: string): Promise<void>;
|
|
28
|
+
sendEvent(roomId: string, type: `ixo.flow.work.${string}`, content: Record<string, unknown>): Promise<void>;
|
|
29
|
+
/** Most recent events in a room, newest first — used for receipt replay checks. */
|
|
30
|
+
recentEvents(roomId: string, limit: number): FlowWorkRoomEvent[];
|
|
31
|
+
}
|
|
32
|
+
/** Wrap a live matrix-js-sdk client in the port interface. */
|
|
33
|
+
export declare function adaptMatrixClient(client: sdk.MatrixClient): FlowWorkMatrixPort;
|
|
34
|
+
//# sourceMappingURL=matrix-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix-port.d.ts","sourceRoot":"","sources":["../src/matrix-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,eAAe,CAAC;AAE1C;;;;GAIG;AAIH,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,cAAc;QACtB,CAAC,GAAG,EAAE,iBAAiB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC3D;CACF;AAID,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAChE,uFAAuF;IACvF,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,SAAS,CACP,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB,MAAM,EAAE,EAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,mFAAmF;IACnF,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAAC;CAClE;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,GAAG,CAAC,YAAY,GACvB,kBAAkB,CAmFpB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Wrap a live matrix-js-sdk client in the port interface. */
|
|
2
|
+
export function adaptMatrixClient(client) {
|
|
3
|
+
return {
|
|
4
|
+
onInvite(handler) {
|
|
5
|
+
const membershipHandler = (event, member) => {
|
|
6
|
+
if (member.membership === 'invite' &&
|
|
7
|
+
member.userId === client.getUserId()) {
|
|
8
|
+
handler({
|
|
9
|
+
roomId: member.roomId,
|
|
10
|
+
inviterUserId: event.getSender() ?? '',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
client.on('RoomMember.membership', membershipHandler);
|
|
15
|
+
return () => client.removeListener('RoomMember.membership', membershipHandler);
|
|
16
|
+
},
|
|
17
|
+
onRoomEvent(handler) {
|
|
18
|
+
const emit = (event) => {
|
|
19
|
+
const roomId = event.getRoomId();
|
|
20
|
+
if (!roomId || event.isEncrypted())
|
|
21
|
+
return;
|
|
22
|
+
handler({
|
|
23
|
+
roomId,
|
|
24
|
+
type: event.getType(),
|
|
25
|
+
content: event.getContent(),
|
|
26
|
+
sender: event.getSender(),
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const timelineHandler = (event) => emit(event);
|
|
30
|
+
// Encrypted rooms surface events again once decrypted.
|
|
31
|
+
const decryptedHandler = (event) => emit(event);
|
|
32
|
+
client.on('Room.timeline', timelineHandler);
|
|
33
|
+
client.on('Event.decrypted', decryptedHandler);
|
|
34
|
+
return () => {
|
|
35
|
+
client.removeListener('Room.timeline', timelineHandler);
|
|
36
|
+
client.removeListener('Event.decrypted', decryptedHandler);
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
async joinRoom(roomId) {
|
|
40
|
+
await client.joinRoom(roomId);
|
|
41
|
+
},
|
|
42
|
+
async sendEvent(roomId, type, content) {
|
|
43
|
+
await client.sendEvent(roomId, type, content);
|
|
44
|
+
},
|
|
45
|
+
recentEvents(roomId, limit) {
|
|
46
|
+
const room = client.getRoom(roomId);
|
|
47
|
+
if (!room)
|
|
48
|
+
return [];
|
|
49
|
+
return room
|
|
50
|
+
.getLiveTimeline()
|
|
51
|
+
.getEvents()
|
|
52
|
+
.slice(-limit)
|
|
53
|
+
.reverse()
|
|
54
|
+
.map((event) => ({
|
|
55
|
+
roomId,
|
|
56
|
+
type: event.getType(),
|
|
57
|
+
content: event.getContent(),
|
|
58
|
+
sender: event.getSender(),
|
|
59
|
+
}));
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=matrix-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix-port.js","sourceRoot":"","sources":["../src/matrix-port.ts"],"names":[],"mappings":"AA6CA,8DAA8D;AAC9D,MAAM,UAAU,iBAAiB,CAC/B,MAAwB;IAExB,OAAO;QACL,QAAQ,CAAC,OAAO;YACd,MAAM,iBAAiB,GAAG,CACxB,KAAsB,EACtB,MAAsB,EAChB,EAAE;gBACR,IACE,MAAM,CAAC,UAAU,KAAK,QAAQ;oBAC9B,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,SAAS,EAAE,EACpC,CAAC;oBACD,OAAO,CAAC;wBACN,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,aAAa,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE;qBACvC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC;YACF,MAAM,CAAC,EAAE,CACP,uBAAyD,EACzD,iBAAiB,CAClB,CAAC;YACF,OAAO,GAAG,EAAE,CACV,MAAM,CAAC,cAAc,CACnB,uBAAyD,EACzD,iBAAiB,CAClB,CAAC;QACN,CAAC;QAED,WAAW,CAAC,OAAO;YACjB,MAAM,IAAI,GAAG,CAAC,KAAsB,EAAQ,EAAE;gBAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE;oBAAE,OAAO;gBAC3C,OAAO,CAAC;oBACN,MAAM;oBACN,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE;oBACrB,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE;oBAC3B,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE;iBAC1B,CAAC,CAAC;YACL,CAAC,CAAC;YACF,MAAM,eAAe,GAAG,CAAC,KAAsB,EAAQ,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtE,uDAAuD;YACvD,MAAM,gBAAgB,GAAG,CAAC,KAAsB,EAAQ,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,EAAE,CAAC,eAAyC,EAAE,eAAe,CAAC,CAAC;YACtE,MAAM,CAAC,EAAE,CACP,iBAAmD,EACnD,gBAAgB,CACjB,CAAC;YACF,OAAO,GAAG,EAAE;gBACV,MAAM,CAAC,cAAc,CACnB,eAAyC,EACzC,eAAe,CAChB,CAAC;gBACF,MAAM,CAAC,cAAc,CACnB,iBAAmD,EACnD,gBAAgB,CACjB,CAAC;YACJ,CAAC,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,MAAM;YACnB,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,IAAuB,EAAE,OAAO;YACtD,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QAED,YAAY,CAAC,MAAM,EAAE,KAAK;YACxB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI;gBAAE,OAAO,EAAE,CAAC;YACrB,OAAO,IAAI;iBACR,eAAe,EAAE;iBACjB,SAAS,EAAE;iBACX,KAAK,CAAC,CAAC,KAAK,CAAC;iBACb,OAAO,EAAE;iBACT,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACf,MAAM;gBACN,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE;gBACrB,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE;gBAC3B,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE;aAC1B,CAAC,CAAC,CAAC;QACR,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The flow-work wire protocol — the Matrix events flow-manager and helper
|
|
4
|
+
* oracles exchange. Mirror of flow-manager's oracle-delegation plugin
|
|
5
|
+
* (apps/app/src/plugins/oracle-delegation/oracle-room.ts). If these shapes
|
|
6
|
+
* change, both sides change together; until this lives in a shared package
|
|
7
|
+
* the duplication is deliberate and must be kept in sync.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FLOW_WORK_ASSIGN_EVENT = "ixo.flow.work.assign";
|
|
10
|
+
export declare const FLOW_WORK_INVOKE_EVENT = "ixo.flow.work.invoke";
|
|
11
|
+
export declare const FLOW_WORK_RECEIPT_EVENT = "ixo.flow.work.receipt";
|
|
12
|
+
export declare const flowWorkInvokeSchema: z.ZodObject<{
|
|
13
|
+
invocationId: z.ZodString;
|
|
14
|
+
capability: z.ZodString;
|
|
15
|
+
actionType: z.ZodString;
|
|
16
|
+
nodeId: z.ZodString;
|
|
17
|
+
inputs: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
18
|
+
delegation: z.ZodString;
|
|
19
|
+
audience: z.ZodString;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type FlowWorkInvoke = z.infer<typeof flowWorkInvokeSchema>;
|
|
22
|
+
export type FlowWorkErrorCode = 'delegation_invalid' | 'delegation_expired' | 'capability_not_declared' | 'input_invalid' | 'tool_error';
|
|
23
|
+
export interface FlowWorkReceipt {
|
|
24
|
+
invocationId: string;
|
|
25
|
+
status: 'success' | 'failure';
|
|
26
|
+
output: Record<string, unknown> | null;
|
|
27
|
+
error: {
|
|
28
|
+
code: FlowWorkErrorCode;
|
|
29
|
+
message: string;
|
|
30
|
+
} | null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,eAAO,MAAM,oBAAoB;;;;;;;;iBAU/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,MAAM,MAAM,iBAAiB,GACzB,oBAAoB,GACpB,oBAAoB,GACpB,yBAAyB,GACzB,eAAe,GACf,YAAY,CAAC;AAEjB,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvC,KAAK,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5D"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The flow-work wire protocol — the Matrix events flow-manager and helper
|
|
4
|
+
* oracles exchange. Mirror of flow-manager's oracle-delegation plugin
|
|
5
|
+
* (apps/app/src/plugins/oracle-delegation/oracle-room.ts). If these shapes
|
|
6
|
+
* change, both sides change together; until this lives in a shared package
|
|
7
|
+
* the duplication is deliberate and must be kept in sync.
|
|
8
|
+
*/
|
|
9
|
+
export const FLOW_WORK_ASSIGN_EVENT = 'ixo.flow.work.assign';
|
|
10
|
+
export const FLOW_WORK_INVOKE_EVENT = 'ixo.flow.work.invoke';
|
|
11
|
+
export const FLOW_WORK_RECEIPT_EVENT = 'ixo.flow.work.receipt';
|
|
12
|
+
export const flowWorkInvokeSchema = z.object({
|
|
13
|
+
invocationId: z.string().trim().min(1),
|
|
14
|
+
capability: z.string().trim().min(1),
|
|
15
|
+
actionType: z.string().trim().min(1),
|
|
16
|
+
nodeId: z.string().trim().min(1),
|
|
17
|
+
inputs: z.record(z.string(), z.unknown()),
|
|
18
|
+
/** Serialized UCAN delegation (base64 CAR) authorizing this work. */
|
|
19
|
+
delegation: z.string().trim().min(1),
|
|
20
|
+
/** DID of the oracle this invocation is addressed to. */
|
|
21
|
+
audience: z.string().trim().min(1),
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAC7D,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAE/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACzC,qEAAqE;IACrE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,yDAAyD;IACzD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACnC,CAAC,CAAC"}
|
package/dist/ucan.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCAN delegation validation for contracted work — same checks the runtime
|
|
3
|
+
* applies to HTTP requests (audience match, chain expiry, signature chain),
|
|
4
|
+
* built from the published @ixo/ucan package.
|
|
5
|
+
*/
|
|
6
|
+
export type DelegationOutcome = {
|
|
7
|
+
ok: true;
|
|
8
|
+
invokerDid: string;
|
|
9
|
+
/**
|
|
10
|
+
* The capability carried by the delegation, including its nb caveats
|
|
11
|
+
* (e.g. the claimSchema a service contract is pinned to). Note:
|
|
12
|
+
* @ixo/ucan's ValidateResult surfaces a single capability — delegations
|
|
13
|
+
* carrying additional capabilities (e.g. supplementary read grants)
|
|
14
|
+
* need upstream validator support before they can be enumerated here.
|
|
15
|
+
*/
|
|
16
|
+
capability?: {
|
|
17
|
+
can: string;
|
|
18
|
+
with: string;
|
|
19
|
+
nb?: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
expiration?: number;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
code: 'delegation_expired' | 'delegation_invalid';
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
export type DelegationValidator = (serializedDelegation: string) => Promise<DelegationOutcome>;
|
|
28
|
+
export declare function buildDelegationValidator(options: {
|
|
29
|
+
/** This oracle's DID — the expected delegation audience. */
|
|
30
|
+
oracleDid: string;
|
|
31
|
+
/** Blocksync GraphQL endpoint for did:ixo key resolution. Optional: did:key verifies locally. */
|
|
32
|
+
blocksyncUrl?: string;
|
|
33
|
+
}): DelegationValidator;
|
|
34
|
+
//# sourceMappingURL=ucan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ucan.d.ts","sourceRoot":"","sources":["../src/ucan.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,MAAM,iBAAiB,GACzB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,MAAM,mBAAmB,GAAG,CAChC,oBAAoB,EAAE,MAAM,KACzB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,mBAAmB,CA8CtB"}
|
package/dist/ucan.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createIxoDIDResolver, createUCANValidator } from '@ixo/ucan';
|
|
2
|
+
export function buildDelegationValidator(options) {
|
|
3
|
+
const validatorPromise = createUCANValidator({
|
|
4
|
+
serverDid: options.oracleDid,
|
|
5
|
+
rootIssuers: [],
|
|
6
|
+
...(options.blocksyncUrl
|
|
7
|
+
? {
|
|
8
|
+
didResolver: createIxoDIDResolver({
|
|
9
|
+
indexerUrl: options.blocksyncUrl,
|
|
10
|
+
}),
|
|
11
|
+
}
|
|
12
|
+
: {}),
|
|
13
|
+
});
|
|
14
|
+
return async (serializedDelegation) => {
|
|
15
|
+
const validator = await validatorPromise;
|
|
16
|
+
const result = await validator.validateDelegation(serializedDelegation);
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
const code = result.error?.code === 'EXPIRED'
|
|
19
|
+
? 'delegation_expired'
|
|
20
|
+
: 'delegation_invalid';
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
code,
|
|
24
|
+
message: `[${result.error?.code ?? 'UNKNOWN'}] ${result.error?.message ?? 'delegation validation failed'}`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (!result.invoker) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
code: 'delegation_invalid',
|
|
31
|
+
message: 'Delegation validated without an invoker DID',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const capability = result.capability;
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
invokerDid: result.invoker,
|
|
38
|
+
capability,
|
|
39
|
+
expiration: result.expiration,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ucan.js.map
|
package/dist/ucan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ucan.js","sourceRoot":"","sources":["../src/ucan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAgCtE,MAAM,UAAU,wBAAwB,CAAC,OAKxC;IACC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;QAC3C,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,EAAE;QACf,GAAG,CAAC,OAAO,CAAC,YAAY;YACtB,CAAC,CAAC;gBACE,WAAW,EAAE,oBAAoB,CAAC;oBAChC,UAAU,EAAE,OAAO,CAAC,YAAY;iBACjC,CAAC;aACH;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;IAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,EAAE;QACpC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,CAAC;QAExE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,GACR,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,SAAS;gBAC9B,CAAC,CAAC,oBAAoB;gBACtB,CAAC,CAAC,oBAAoB,CAAC;YAC3B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI;gBACJ,OAAO,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,8BAA8B,EAAE;aAC3G,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,6CAA6C;aACvD,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAEb,CAAC;QACd,OAAO;YACL,EAAE,EAAE,IAAI;YACR,UAAU,EAAE,MAAM,CAAC,OAAO;YAC1B,UAAU;YACV,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ixo/flow-work",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Oracle-side flow-work plugin: accept UCAN-delegated ixo.flow.work invocations from the IXO flow-manager over Matrix and post receipts.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/ixoworld/flow-manager-oracle.git",
|
|
24
|
+
"directory": "packages/flow-work"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"zod": "^4.4.3"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@ixo/oracle-runtime": ">=1.3.4",
|
|
34
|
+
"@ixo/ucan": ">=2.0.0",
|
|
35
|
+
"@nestjs/common": ">=11.0.0",
|
|
36
|
+
"matrix-js-sdk": ">=37.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@changesets/cli": "^2.31.0",
|
|
40
|
+
"@ixo/oracle-runtime": "^1.3.4",
|
|
41
|
+
"@ixo/ucan": "^2.0.0",
|
|
42
|
+
"@nestjs/common": "^11.1.19",
|
|
43
|
+
"@types/node": "^22.10.7",
|
|
44
|
+
"matrix-js-sdk": "^37.11.0",
|
|
45
|
+
"typescript": "^5.7.3",
|
|
46
|
+
"vitest": "^3.2.4"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.build.json",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"changeset": "changeset",
|
|
53
|
+
"version": "changeset version",
|
|
54
|
+
"release": "changeset publish"
|
|
55
|
+
}
|
|
56
|
+
}
|