@bluefly/openstandardagents 0.5.0 → 0.5.1
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/.version.json +3 -3
- package/CHANGELOG.md +43 -12
- package/README.md +31 -26
- package/bin/postinstall +0 -0
- package/dist/.version.json +3 -3
- package/dist/adapters/a2a/a2a-protocol.js +4 -2
- package/dist/adapters/a2a/a2a-tool.js +4 -2
- package/dist/adapters/a2a/mcp-integration.d.ts +2 -1
- package/dist/adapters/a2a/mcp-integration.js +6 -3
- package/dist/adapters/browser/browser-exporter.d.ts +26 -0
- package/dist/adapters/browser/browser-exporter.js +73 -0
- package/dist/adapters/browser/browser-runner.d.ts +23 -0
- package/dist/adapters/browser/browser-runner.js +46 -0
- package/dist/adapters/browser/index.d.ts +9 -0
- package/dist/adapters/browser/index.js +9 -0
- package/dist/adapters/docker/index.d.ts +2 -0
- package/dist/adapters/docker/index.js +2 -0
- package/dist/adapters/docker/openclaw-bridge.d.ts +57 -0
- package/dist/adapters/docker/openclaw-bridge.js +173 -0
- package/dist/adapters/drupal/index.d.ts +1 -0
- package/dist/adapters/drupal/index.js +2 -0
- package/dist/adapters/drupal/twig-renderer.d.ts +23 -0
- package/dist/adapters/drupal/twig-renderer.js +99 -0
- package/dist/adapters/gitlab/agent-generator.js +2 -1
- package/dist/api/index.js +2 -1
- package/dist/api/routes/mcp.router.js +3 -1
- package/dist/api/routes/wizard.router.js +3 -1
- package/dist/cli/commands/agent/discover-type.command.js +1 -1
- package/dist/cli/commands/agent-card.command.js +37 -10
- package/dist/cli/commands/agents-sync.command.d.ts +2 -2
- package/dist/cli/commands/agents-sync.command.js +27 -17
- package/dist/cli/commands/catalog/config.js +1 -1
- package/dist/cli/commands/catalog/validate.command.js +2 -2
- package/dist/cli/commands/config.command.js +2 -2
- package/dist/cli/commands/daemon.command.js +32 -8
- package/dist/cli/commands/discover.d.ts +1 -1
- package/dist/cli/commands/discover.js +16 -8
- package/dist/cli/commands/economics.command.d.ts +9 -0
- package/dist/cli/commands/economics.command.js +113 -0
- package/dist/cli/commands/export.command.js +6 -3
- package/dist/cli/commands/mcp.command.js +3 -1
- package/dist/cli/commands/memory.command.d.ts +18 -0
- package/dist/cli/commands/memory.command.js +168 -0
- package/dist/cli/commands/publish.command.js +7 -4
- package/dist/cli/commands/serve-builder-routes.js +1 -1
- package/dist/cli/commands/usie-skills.command.d.ts +24 -0
- package/dist/cli/commands/usie-skills.command.js +297 -0
- package/dist/cli/commands/validate.command.js +8 -1
- package/dist/cli/commands/verify.d.ts +3 -3
- package/dist/cli/commands/verify.js +12 -6
- package/dist/cli/commands/workspace.command.d.ts +1 -0
- package/dist/cli/commands/workspace.command.js +28 -4
- package/dist/cli/index.js +12 -0
- package/dist/cli/workspace-validate.d.ts +23 -0
- package/dist/cli/workspace-validate.js +117 -0
- package/dist/data/platform-matrix.js +1 -4
- package/dist/generated/types.d.ts +97 -97
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mcp-server/index.js +658 -982
- package/dist/mesh/discovery-gkg.d.ts +26 -0
- package/dist/mesh/discovery-gkg.js +92 -0
- package/dist/messenger/Handler/AgentBatchHandler.js +3 -2
- package/dist/messenger/Handler/AgentExecutionHandler.js +6 -1
- package/dist/package.json +20 -4
- package/dist/sdks/shared/types.d.ts +1 -1
- package/dist/services/agent-card-generator.js +6 -2
- package/dist/services/daemon/audit-log.service.js +3 -1
- package/dist/services/daemon/execution.service.js +8 -4
- package/dist/services/daemon/fs-watcher.service.js +6 -7
- package/dist/services/daemon/pairing.service.js +2 -1
- package/dist/services/daemon/skill-aggregator.service.js +105 -21
- package/dist/services/daemon/sse-endpoints.js +1 -1
- package/dist/services/daemon/ws-server.js +10 -3
- package/dist/services/governance/cedar-provider.js +12 -8
- package/dist/services/governance/cedar-validator.service.js +1 -1
- package/dist/services/mcp/bridge.service.js +40 -9
- package/dist/services/openapi-extensions-validation.d.ts +20 -0
- package/dist/services/openapi-extensions-validation.js +193 -0
- package/dist/services/release-automation/merge-request.service.d.ts +4 -4
- package/dist/services/release-automation/release-buttons.js +3 -3
- package/dist/services/release-automation/schemas/release.schema.d.ts +3 -3
- package/dist/services/runtime/openai.adapter.d.ts +46 -13
- package/dist/services/runtime/openai.adapter.js +169 -131
- package/dist/services/skill-registry.service.d.ts +1 -1
- package/dist/services/skills-pipeline/skills-research.service.js +47 -7
- package/dist/services/trust/trust.service.js +6 -4
- package/dist/services/validation-zod.service.js +3 -22
- package/dist/services/validators/index.d.ts +1 -0
- package/dist/services/validators/index.js +1 -0
- package/dist/services/validators/registry.d.ts +21 -0
- package/dist/services/validators/registry.js +42 -0
- package/dist/skills/test-skill/package.json +1 -1
- package/dist/spec/extensions/cognition.schema.json +87 -0
- package/dist/spec/layer4-economics/duadp-examples.json +44 -0
- package/dist/spec/v0.4/agent.schema.json +14 -0
- package/dist/spec/v0.5/agent-builder-openapi.yaml +230 -0
- package/dist/spec/v0.5/agent.schema.json +32 -1
- package/dist/spec/v0.5/extensions/cognition/cognition.schema.json +78 -1
- package/dist/spec/v0.5/extensions/economics/context-pack.schema.json +91 -0
- package/dist/spec/v0.5/extensions/economics/execution-profile.schema.json +148 -0
- package/dist/spec/v0.5/extensions/economics/failure-semantics.schema.json +32 -0
- package/dist/spec/v0.5/extensions/economics/replay-packet.schema.json +120 -0
- package/dist/spec/v0.5/extensions/mcp/README.md +1 -1
- package/dist/spec/v0.5/memory-hierarchy.yaml +120 -0
- package/dist/spec/v1/agent-card.schema.json +254 -0
- package/dist/types/cognition.zod.d.ts +312 -0
- package/dist/types/cognition.zod.js +223 -0
- package/dist/types/identity.zod.d.ts +5 -5
- package/dist/types/index.d.ts +53 -7
- package/dist/types/index.js +4 -2
- package/dist/types/personality.zod.d.ts +3 -3
- package/dist/utils/http-client.d.ts +22 -0
- package/dist/utils/http-client.js +51 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/proxy-resolver.d.ts +36 -0
- package/dist/utils/proxy-resolver.js +59 -0
- package/dist/utils/user-agent.d.ts +11 -0
- package/dist/utils/user-agent.js +17 -0
- package/dist/validation/version-compliance.js +1 -1
- package/examples/agentscope/react-assistant/README.md +1 -1
- package/examples/agentscope/react-assistant/agent.ossa.yaml +1 -1
- package/examples/drupal/drupal-contributor-agent/.eslintrc.json +58 -0
- package/examples/drupal/drupal-contributor-agent/.prettierrc.json +10 -0
- package/examples/drupal/drupal-contributor-agent/package.json +55 -0
- package/examples/drupal/drupal-contributor-agent/src/core/index.ts +10 -0
- package/examples/drupal/drupal-contributor-agent/src/index.ts +17 -0
- package/examples/drupal/drupal-contributor-agent/src/types/index.ts +180 -0
- package/examples/drupal/drupal-contributor-agent/tsconfig.json +36 -0
- package/examples/getting-started/01-minimal-agent.ossa.yaml +1 -1
- package/examples/getting-started/02-agent-with-tools.ossa.yaml +1 -1
- package/examples/getting-started/03-agent-with-safety.ossa.yaml +1 -1
- package/examples/getting-started/04-agent-with-messaging.ossa.yaml +1 -1
- package/examples/getting-started/05-workflow-composition.ossa.yaml +1 -1
- package/examples/getting-started/hello-world-complete.ossa.yaml +1 -1
- package/examples/reference-implementations/python-client/examples/basic_usage.py +0 -0
- package/examples/reference-implementations/python-client/examples/publish_agent.py +0 -0
- package/openapi/agent-cognition-sessions.yaml +580 -0
- package/openapi/agent-crud.yaml +20 -20
- package/openapi/core/ossa-registry-api.openapi.yaml +1 -1
- package/openapi/ossa-cli-enhancements.openapi.yaml +1 -1
- package/openapi/release-automation.openapi.yaml +1 -1
- package/openapi/schemas/common/economics.yaml +98 -0
- package/openapi/uadp-asyncapi.yaml +1 -1
- package/openapi/uadp-openapi.yaml +2 -2
- package/package.json +114 -96
- package/spec/extensions/cognition.schema.json +87 -0
- package/spec/layer4-economics/duadp-examples.json +44 -0
- package/spec/v0.4/agent.schema.json +14 -0
- package/spec/v0.5/agent-builder-openapi.yaml +230 -0
- package/spec/v0.5/agent.schema.json +32 -1
- package/spec/v0.5/extensions/cognition/cognition.schema.json +78 -1
- package/spec/v0.5/extensions/economics/context-pack.schema.json +91 -0
- package/spec/v0.5/extensions/economics/execution-profile.schema.json +148 -0
- package/spec/v0.5/extensions/economics/failure-semantics.schema.json +32 -0
- package/spec/v0.5/extensions/economics/replay-packet.schema.json +120 -0
- package/spec/v0.5/extensions/mcp/README.md +1 -1
- package/spec/v0.5/memory-hierarchy.yaml +120 -0
- package/spec/v1/agent-card.schema.json +254 -0
- package/dist/adapters/a2a/__tests__/mcp-integration.spec.d.ts +0 -5
- package/dist/adapters/a2a/__tests__/mcp-integration.spec.js +0 -268
- package/dist/adapters/a2a/__tests__/mcp-transport.spec.d.ts +0 -5
- package/dist/adapters/a2a/__tests__/mcp-transport.spec.js +0 -203
- package/dist/mcp-server/__tests__/mcp-server.spec.d.ts +0 -8
- package/dist/mcp-server/__tests__/mcp-server.spec.js +0 -566
- package/dist/validation/__tests__/error-codes.test.d.ts +0 -5
- package/dist/validation/__tests__/error-codes.test.js +0 -252
- package/dist/version-management/core/version-manager.test.d.ts +0 -2
- package/dist/version-management/core/version-manager.test.js +0 -210
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AgentCard } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* GKG Matchmaker Discovery Provider
|
|
4
|
+
*
|
|
5
|
+
* Replaces static registry discovery with semantic Contract Matchmaking via the Global Knowledge Graph.
|
|
6
|
+
* Resolves agents by their IO contracts (emits/consumes) and Execution Economics.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GkgMatchmaker {
|
|
9
|
+
private gkgEndpoint;
|
|
10
|
+
/**
|
|
11
|
+
* @param gkgEndpoint - GKG API base URL.
|
|
12
|
+
* Defaults to `OSSA_GKG_ENDPOINT` env var.
|
|
13
|
+
* Set this to your own GKG-compatible knowledge graph endpoint.
|
|
14
|
+
*/
|
|
15
|
+
constructor(gkgEndpoint?: string);
|
|
16
|
+
/**
|
|
17
|
+
* Discover agents that satisfy a specific IO contract and economics profile.
|
|
18
|
+
*/
|
|
19
|
+
discoverByContract(emitsSchema?: string, consumesSchema?: string, maxCostUsd?: number): Promise<AgentCard[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Initiate a Lead Selection Market bid request for a task
|
|
22
|
+
*/
|
|
23
|
+
requestMarketBids(taskContext: any, requiredContextPacks: string[]): Promise<any[]>;
|
|
24
|
+
private mapToAgentCard;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=discovery-gkg.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GKG Matchmaker Discovery Provider
|
|
3
|
+
*
|
|
4
|
+
* Replaces static registry discovery with semantic Contract Matchmaking via the Global Knowledge Graph.
|
|
5
|
+
* Resolves agents by their IO contracts (emits/consumes) and Execution Economics.
|
|
6
|
+
*/
|
|
7
|
+
export class GkgMatchmaker {
|
|
8
|
+
gkgEndpoint;
|
|
9
|
+
/**
|
|
10
|
+
* @param gkgEndpoint - GKG API base URL.
|
|
11
|
+
* Defaults to `OSSA_GKG_ENDPOINT` env var.
|
|
12
|
+
* Set this to your own GKG-compatible knowledge graph endpoint.
|
|
13
|
+
*/
|
|
14
|
+
constructor(gkgEndpoint) {
|
|
15
|
+
this.gkgEndpoint =
|
|
16
|
+
gkgEndpoint ??
|
|
17
|
+
process.env.OSSA_GKG_ENDPOINT ??
|
|
18
|
+
'';
|
|
19
|
+
if (!this.gkgEndpoint) {
|
|
20
|
+
throw new Error('GkgMatchmaker requires a GKG endpoint. ' +
|
|
21
|
+
'Pass it to the constructor or set the OSSA_GKG_ENDPOINT environment variable.');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Discover agents that satisfy a specific IO contract and economics profile.
|
|
26
|
+
*/
|
|
27
|
+
async discoverByContract(emitsSchema, consumesSchema, maxCostUsd) {
|
|
28
|
+
const query = {
|
|
29
|
+
type: 'agent',
|
|
30
|
+
has_capability: true,
|
|
31
|
+
filters: {
|
|
32
|
+
...(emitsSchema && { 'extensions.statemesh.emits.schema': emitsSchema }),
|
|
33
|
+
...(consumesSchema && { 'extensions.statemesh.consumes.schema': consumesSchema }),
|
|
34
|
+
...(maxCostUsd && { 'economics.max_cost_usd': { $lte: maxCostUsd } })
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(`${this.gkgEndpoint}/search/semantic`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: { 'Content-Type': 'application/json' },
|
|
41
|
+
body: JSON.stringify(query)
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`GKG Semantic Search failed: ${response.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
const results = await response.json();
|
|
47
|
+
return results.map((r) => this.mapToAgentCard(r));
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.error('GKG Matchmaker discovery failed', e);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initiate a Lead Selection Market bid request for a task
|
|
56
|
+
*/
|
|
57
|
+
async requestMarketBids(taskContext, requiredContextPacks) {
|
|
58
|
+
try {
|
|
59
|
+
const response = await fetch(`${this.gkgEndpoint}/market/bids/request`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ taskContext, requiredContextPacks })
|
|
63
|
+
});
|
|
64
|
+
return await response.json();
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error('Failed to request market bids', e);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
mapToAgentCard(raw) {
|
|
72
|
+
return {
|
|
73
|
+
uri: raw.uri || (raw.metadata?.name ? `did:web:${raw.metadata.name}` : undefined),
|
|
74
|
+
name: raw.metadata?.name || 'anonymous',
|
|
75
|
+
version: raw.metadata?.version || '1.0.0',
|
|
76
|
+
ossaVersion: raw.apiVersion || 'ossa/v0.5.1',
|
|
77
|
+
gaid: raw.gaid,
|
|
78
|
+
endpoints: raw.endpoints || {},
|
|
79
|
+
transport: raw.transport || ['http'],
|
|
80
|
+
authentication: raw.authentication || ['none'],
|
|
81
|
+
encryption: raw.encryption || { tlsRequired: true, minTlsVersion: '1.3' },
|
|
82
|
+
capabilities: raw.spec?.tools?.map((t) => t.name) || [],
|
|
83
|
+
status: raw.metadata?.status || 'healthy',
|
|
84
|
+
metadata: {
|
|
85
|
+
description: raw.metadata?.description,
|
|
86
|
+
author: raw.metadata?.author,
|
|
87
|
+
...(raw.metadata || {})
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=discovery-gkg.js.map
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @package @bluefly/openstandardagents
|
|
8
8
|
* @since 0.5.0
|
|
9
9
|
*/
|
|
10
|
+
import { trace } from '@opentelemetry/api';
|
|
10
11
|
export class AgentBatchHandler {
|
|
11
12
|
deps;
|
|
12
13
|
constructor(deps) {
|
|
@@ -50,8 +51,8 @@ export class AgentBatchHandler {
|
|
|
50
51
|
duration,
|
|
51
52
|
},
|
|
52
53
|
};
|
|
53
|
-
// Store batch result
|
|
54
|
-
const batchId = context.requestId ?? `batch-${Date.now()}`;
|
|
54
|
+
// Store batch result — prefer OTel traceId so the storage key correlates with distributed traces
|
|
55
|
+
const batchId = trace.getActiveSpan()?.spanContext().traceId ?? context.requestId ?? `batch-${Date.now()}`;
|
|
55
56
|
await this.deps.resultStorage.store(batchId, batchResult);
|
|
56
57
|
// Dispatch completion event
|
|
57
58
|
await this.deps.eventDispatcher.dispatch('agent.batch.complete', {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @package @bluefly/openstandardagents
|
|
9
9
|
* @since 0.5.0
|
|
10
10
|
*/
|
|
11
|
+
import { trace } from '@opentelemetry/api';
|
|
11
12
|
export class AgentExecutionHandler {
|
|
12
13
|
deps;
|
|
13
14
|
constructor(deps) {
|
|
@@ -21,11 +22,15 @@ export class AgentExecutionHandler {
|
|
|
21
22
|
const agentId = message.getAgentId();
|
|
22
23
|
const input = message.getInput();
|
|
23
24
|
const context = message.getContext();
|
|
25
|
+
const span = trace.getActiveSpan();
|
|
26
|
+
if (context.requestId) {
|
|
27
|
+
span?.setAttribute('request.id', context.requestId);
|
|
28
|
+
}
|
|
24
29
|
this.deps.logger.info('Starting agent execution', {
|
|
25
30
|
agentId,
|
|
26
31
|
userId: context.userId,
|
|
27
32
|
sessionId: context.sessionId,
|
|
28
|
-
requestId: context.requestId,
|
|
33
|
+
requestId: span?.spanContext().traceId ?? context.requestId,
|
|
29
34
|
});
|
|
30
35
|
try {
|
|
31
36
|
// Load agent manifest
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluefly/openstandardagents",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./schema": "./spec/v0.5/agent.schema.json",
|
|
6
6
|
"./schema/v0.4": "./spec/v0.4/agent.schema.json",
|
|
@@ -74,6 +74,11 @@
|
|
|
74
74
|
"types": "./dist/services/trust/trust.service.d.ts",
|
|
75
75
|
"import": "./dist/services/trust/trust.service.js",
|
|
76
76
|
"require": "./dist/services/trust/trust.service.js"
|
|
77
|
+
},
|
|
78
|
+
"./workspace-validate": {
|
|
79
|
+
"types": "./dist/cli/workspace-validate.d.ts",
|
|
80
|
+
"import": "./dist/cli/workspace-validate.js",
|
|
81
|
+
"require": "./dist/cli/workspace-validate.js"
|
|
77
82
|
}
|
|
78
83
|
},
|
|
79
84
|
"description": "OSSA - Open Standard for Software Agents. Infrastructure bridge between agent protocols (MCP, A2A) and deployment platforms. Define once in YAML, export to 9+ platforms (Docker, Kubernetes, LangChain, CrewAI, Claude Skills, etc.).",
|
|
@@ -230,7 +235,7 @@
|
|
|
230
235
|
"postinstall": "node bin/postinstall || true",
|
|
231
236
|
"// RELEASE & PUBLISH": "",
|
|
232
237
|
"prepublishOnly": "publint && npm run test:unit && npm run build",
|
|
233
|
-
"validate:deps": "depcheck --ignore-dirs=examples --ignores='@anthropic-ai/claude-agent-sdk,@langchain/openai,@openai/agents,@temporalio/activity,@temporalio/workflow,pino-pretty,readline,@types/*,ts-jest,eslint*,prettier*,lefthook,jest*,vitest,semantic-release*,@semantic-release/*,keep-a-changelog,jest-junit,publint,conventional-changelog-conventionalcommits,ajv-cli,json-schema-to-typescript,json-schema-to-zod,ts-node,@playwright/test,@langchain/anthropic,@langchain/core,langchain'",
|
|
238
|
+
"validate:deps": "depcheck --ignore-dirs=examples --ignores='@anthropic-ai/claude-agent-sdk,@langchain/openai,@openai/agents,@temporalio/activity,@temporalio/workflow,pino-pretty,readline,@types/*,ts-jest,eslint*,prettier*,lefthook,jest*,vitest,semantic-release*,@semantic-release/*,keep-a-changelog,jest-junit,publint,conventional-changelog-conventionalcommits,ajv-cli,json-schema-to-typescript,json-schema-to-zod,ts-node,@playwright/test,@langchain/anthropic,@langchain/core,langchain,agent-browser,@ai-sdk/anthropic,@ai-sdk/google'",
|
|
234
239
|
"validate:package": "publint",
|
|
235
240
|
"publish:dry-run": "npm publish --dry-run",
|
|
236
241
|
"release:check": "npm run typecheck && npm run test:unit && npm run validate:schema && publint",
|
|
@@ -238,9 +243,13 @@
|
|
|
238
243
|
"release:semantic:dry": "npx semantic-release --dry-run"
|
|
239
244
|
},
|
|
240
245
|
"dependencies": {
|
|
246
|
+
"@ai-sdk/anthropic": "^3.0.58",
|
|
247
|
+
"@ai-sdk/google": "^3.0.43",
|
|
248
|
+
"@ai-sdk/openai": "^3.0.41",
|
|
241
249
|
"@anthropic-ai/claude-agent-sdk": "^0.2.50",
|
|
242
250
|
"@anthropic-ai/sdk": "^0.71.0",
|
|
243
251
|
"@aws-sdk/client-bedrock-runtime": "^3.955.0",
|
|
252
|
+
"@better-openclaw/core": "^1.0.30",
|
|
244
253
|
"@cedar-policy/cedar-wasm": "^4.9.1",
|
|
245
254
|
"@gitbeaker/node": "^35.8.1",
|
|
246
255
|
"@gitbeaker/rest": "^43.8.0",
|
|
@@ -269,6 +278,8 @@
|
|
|
269
278
|
"@temporalio/workflow": "^1.11.3",
|
|
270
279
|
"@types/tar": "^6.1.13",
|
|
271
280
|
"@types/ws": "^8.18.1",
|
|
281
|
+
"agent-browser": "^0.20.11",
|
|
282
|
+
"ai": "^6.0.116",
|
|
272
283
|
"ajv": "^8.12.0",
|
|
273
284
|
"ajv-formats": "^3.0.1",
|
|
274
285
|
"axios": "^1.12.2",
|
|
@@ -294,15 +305,19 @@
|
|
|
294
305
|
"pino": "^10.3.0",
|
|
295
306
|
"pino-pretty": "^13.1.3",
|
|
296
307
|
"prom-client": "^15.1.3",
|
|
308
|
+
"proxy-agent": "^7.0.0",
|
|
297
309
|
"readline": "^1.3.0",
|
|
298
310
|
"reflect-metadata": "^0.2.2",
|
|
299
311
|
"semver": "^7.7.3",
|
|
300
312
|
"tar": "^7.5.11",
|
|
301
|
-
"
|
|
313
|
+
"twig": "^2.0.0",
|
|
314
|
+
"twig-drupal-filters": "^3.2.0",
|
|
315
|
+
"universal-user-agent": "^7.0.3",
|
|
316
|
+
"uuid": "^11.1.0",
|
|
302
317
|
"web-did-resolver": "^2.0.32",
|
|
303
318
|
"ws": "^8.19.0",
|
|
304
319
|
"yaml": "^2.3.0",
|
|
305
|
-
"zod": "^4.
|
|
320
|
+
"zod": "^4.3.6"
|
|
306
321
|
},
|
|
307
322
|
"devDependencies": {
|
|
308
323
|
"@bluefly/duadp": "^0.1.4",
|
|
@@ -323,6 +338,7 @@
|
|
|
323
338
|
"@types/pg": "^8.15.5",
|
|
324
339
|
"@types/redis": "^4.0.10",
|
|
325
340
|
"@types/semver": "^7.7.1",
|
|
341
|
+
"@types/twig": "^1.12.17",
|
|
326
342
|
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
327
343
|
"@typescript-eslint/parser": "^8.48.0",
|
|
328
344
|
"ajv-cli": "^5.0.0",
|
|
@@ -29,8 +29,8 @@ export declare const StateConfigSchema: z.ZodObject<{
|
|
|
29
29
|
enabled: z.ZodBoolean;
|
|
30
30
|
type: z.ZodOptional<z.ZodEnum<{
|
|
31
31
|
database: "database";
|
|
32
|
-
memory: "memory";
|
|
33
32
|
file: "file";
|
|
33
|
+
memory: "memory";
|
|
34
34
|
}>>;
|
|
35
35
|
provider: z.ZodOptional<z.ZodString>;
|
|
36
36
|
}, z.core.$strip>;
|
|
@@ -88,8 +88,12 @@ export class AgentCardGenerator {
|
|
|
88
88
|
name,
|
|
89
89
|
version,
|
|
90
90
|
ossaVersion: manifest.apiVersion || 'ossa/v0.4',
|
|
91
|
-
...(manifest.metadata?.identity?.uuid
|
|
92
|
-
|
|
91
|
+
...(manifest.metadata?.identity?.uuid
|
|
92
|
+
? { uuid: manifest.metadata.identity.uuid }
|
|
93
|
+
: {}),
|
|
94
|
+
...(manifest.metadata?.identity?.gaid
|
|
95
|
+
? { gaid: manifest.metadata.identity.gaid }
|
|
96
|
+
: {}),
|
|
93
97
|
// Taxonomy — what kind of agent
|
|
94
98
|
...(taxonomy ? { taxonomy } : {}),
|
|
95
99
|
// Capabilities & tools
|
|
@@ -64,7 +64,9 @@ let AuditLogService = class AuditLogService {
|
|
|
64
64
|
}
|
|
65
65
|
catch (err) {
|
|
66
66
|
// File doesn't exist yet or read error — return empty
|
|
67
|
-
if (err instanceof Error &&
|
|
67
|
+
if (err instanceof Error &&
|
|
68
|
+
'code' in err &&
|
|
69
|
+
err.code === 'ENOENT') {
|
|
68
70
|
return [];
|
|
69
71
|
}
|
|
70
72
|
process.stderr.write(`[audit-log] read failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -38,14 +38,19 @@ try {
|
|
|
38
38
|
const eqIdx = trimmed.indexOf('=');
|
|
39
39
|
if (eqIdx > 0) {
|
|
40
40
|
const key = trimmed.slice(0, eqIdx).trim();
|
|
41
|
-
const val = trimmed
|
|
41
|
+
const val = trimmed
|
|
42
|
+
.slice(eqIdx + 1)
|
|
43
|
+
.trim()
|
|
44
|
+
.replace(/^["']|["']$/g, '');
|
|
42
45
|
if (!process.env[key])
|
|
43
46
|
process.env[key] = val;
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
|
-
catch {
|
|
51
|
+
catch {
|
|
52
|
+
/* .env loading is best-effort */
|
|
53
|
+
}
|
|
49
54
|
const MAX_CONCURRENT = 3;
|
|
50
55
|
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
51
56
|
const SUPPORTED_RUNTIMES = [
|
|
@@ -117,8 +122,7 @@ let ExecutionService = class ExecutionService extends EventEmitter {
|
|
|
117
122
|
}, timeout);
|
|
118
123
|
this.timeoutTimers.set(executionId, timer);
|
|
119
124
|
// Run asynchronously — don't block the caller
|
|
120
|
-
this.runExecution(executionId, manifestPath, input, selectedRuntime, abortController.signal)
|
|
121
|
-
.catch((err) => {
|
|
125
|
+
this.runExecution(executionId, manifestPath, input, selectedRuntime, abortController.signal).catch((err) => {
|
|
122
126
|
// Safety net for unhandled errors
|
|
123
127
|
logger.error({ err, executionId }, 'Unhandled execution error');
|
|
124
128
|
});
|
|
@@ -22,11 +22,7 @@ const WATCH_PATTERNS = [
|
|
|
22
22
|
'**/*.skill.yml',
|
|
23
23
|
'**/SKILL.md',
|
|
24
24
|
];
|
|
25
|
-
const IGNORED = [
|
|
26
|
-
'**/node_modules/**',
|
|
27
|
-
'**/dist/**',
|
|
28
|
-
'**/.git/**',
|
|
29
|
-
];
|
|
25
|
+
const IGNORED = ['**/node_modules/**', '**/dist/**', '**/.git/**'];
|
|
30
26
|
const DEBOUNCE_MS = 300;
|
|
31
27
|
let FileWatcherService = class FileWatcherService {
|
|
32
28
|
watcher = null;
|
|
@@ -101,7 +97,8 @@ let FileWatcherService = class FileWatcherService {
|
|
|
101
97
|
isPathSafe(filePath) {
|
|
102
98
|
const resolved = path.resolve(filePath);
|
|
103
99
|
// Must be inside workspace root
|
|
104
|
-
if (!resolved.startsWith(this.workspaceRoot + path.sep) &&
|
|
100
|
+
if (!resolved.startsWith(this.workspaceRoot + path.sep) &&
|
|
101
|
+
resolved !== this.workspaceRoot) {
|
|
105
102
|
return false;
|
|
106
103
|
}
|
|
107
104
|
// Get relative path and check each segment for dotfiles (except .ossa.yaml)
|
|
@@ -138,7 +135,9 @@ let FileWatcherService = class FileWatcherService {
|
|
|
138
135
|
else {
|
|
139
136
|
const fileStats = stats ?? this.safeStatSync(absolutePath);
|
|
140
137
|
this.trackedFiles.set(relativePath, {
|
|
141
|
-
modified: fileStats
|
|
138
|
+
modified: fileStats
|
|
139
|
+
? new Date(fileStats.mtime).toISOString()
|
|
140
|
+
: new Date().toISOString(),
|
|
142
141
|
size: fileStats?.size ?? 0,
|
|
143
142
|
});
|
|
144
143
|
}
|
|
@@ -33,7 +33,8 @@ let PairingService = class PairingService {
|
|
|
33
33
|
*/
|
|
34
34
|
getCurrentCode() {
|
|
35
35
|
const now = Date.now();
|
|
36
|
-
if (!this.currentCode ||
|
|
36
|
+
if (!this.currentCode ||
|
|
37
|
+
now - this.codeGeneratedAt >= CODE_ROTATION_INTERVAL_MS) {
|
|
37
38
|
this.currentCode = this.generateCode();
|
|
38
39
|
this.codeGeneratedAt = now;
|
|
39
40
|
}
|
|
@@ -22,18 +22,78 @@ import * as path from 'node:path';
|
|
|
22
22
|
import { safeParseYAML } from '../../utils/yaml-parser.js';
|
|
23
23
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
24
24
|
const BUILTIN_SKILLS = [
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
{
|
|
26
|
+
name: 'web-search',
|
|
27
|
+
source: 'builtin',
|
|
28
|
+
description: 'Search the web for information',
|
|
29
|
+
tags: ['search', 'web'],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'code-review',
|
|
33
|
+
source: 'builtin',
|
|
34
|
+
description: 'Review code for quality, security, and best practices',
|
|
35
|
+
tags: ['code', 'review', 'quality'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'text-summary',
|
|
39
|
+
source: 'builtin',
|
|
40
|
+
description: 'Summarize text content into concise form',
|
|
41
|
+
tags: ['text', 'summary', 'nlp'],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'data-analysis',
|
|
45
|
+
source: 'builtin',
|
|
46
|
+
description: 'Analyze structured and unstructured data',
|
|
47
|
+
tags: ['data', 'analysis', 'statistics'],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'image-generation',
|
|
51
|
+
source: 'builtin',
|
|
52
|
+
description: 'Generate images from text descriptions',
|
|
53
|
+
tags: ['image', 'generation', 'creative'],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'translation',
|
|
57
|
+
source: 'builtin',
|
|
58
|
+
description: 'Translate text between languages',
|
|
59
|
+
tags: ['translation', 'language', 'nlp'],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'file-management',
|
|
63
|
+
source: 'builtin',
|
|
64
|
+
description: 'Read, write, and manage files in the workspace',
|
|
65
|
+
tags: ['file', 'io', 'workspace'],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'api-integration',
|
|
69
|
+
source: 'builtin',
|
|
70
|
+
description: 'Integrate with external REST and GraphQL APIs',
|
|
71
|
+
tags: ['api', 'integration', 'http'],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'database-query',
|
|
75
|
+
source: 'builtin',
|
|
76
|
+
description: 'Query and manage database connections',
|
|
77
|
+
tags: ['database', 'sql', 'query'],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'monitoring',
|
|
81
|
+
source: 'builtin',
|
|
82
|
+
description: 'Monitor system health, metrics, and alerts',
|
|
83
|
+
tags: ['monitoring', 'observability', 'health'],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'testing',
|
|
87
|
+
source: 'builtin',
|
|
88
|
+
description: 'Run and manage test suites',
|
|
89
|
+
tags: ['testing', 'quality', 'ci'],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'documentation',
|
|
93
|
+
source: 'builtin',
|
|
94
|
+
description: 'Generate and maintain documentation',
|
|
95
|
+
tags: ['docs', 'documentation', 'writing'],
|
|
96
|
+
},
|
|
37
97
|
];
|
|
38
98
|
let SkillAggregatorService = class SkillAggregatorService {
|
|
39
99
|
cache = new Map();
|
|
@@ -152,7 +212,12 @@ let SkillAggregatorService = class SkillAggregatorService {
|
|
|
152
212
|
// --- Private loaders ---
|
|
153
213
|
async loadLocal() {
|
|
154
214
|
const patterns = ['**/*.skill.yaml', '**/*.skill.yml', '**/SKILL.md'];
|
|
155
|
-
const ignorePatterns = [
|
|
215
|
+
const ignorePatterns = [
|
|
216
|
+
'**/node_modules/**',
|
|
217
|
+
'**/dist/**',
|
|
218
|
+
'**/.git/**',
|
|
219
|
+
'**/coverage/**',
|
|
220
|
+
];
|
|
156
221
|
const files = await fg(patterns, {
|
|
157
222
|
cwd: this.workspacePath,
|
|
158
223
|
ignore: ignorePatterns,
|
|
@@ -166,25 +231,39 @@ let SkillAggregatorService = class SkillAggregatorService {
|
|
|
166
231
|
// SKILL.md — extract name from first heading, rest is description
|
|
167
232
|
const lines = content.split('\n');
|
|
168
233
|
const heading = lines.find((l) => l.startsWith('#'));
|
|
169
|
-
const name = heading
|
|
234
|
+
const name = heading
|
|
235
|
+
? heading.replace(/^#+\s*/, '').trim()
|
|
236
|
+
: path.basename(path.dirname(file));
|
|
170
237
|
const description = lines
|
|
171
238
|
.filter((l) => !l.startsWith('#') && l.trim())
|
|
172
239
|
.slice(0, 3)
|
|
173
240
|
.join(' ')
|
|
174
241
|
.trim();
|
|
175
|
-
skills.push({
|
|
242
|
+
skills.push({
|
|
243
|
+
name,
|
|
244
|
+
source: 'local',
|
|
245
|
+
description: description || 'Local skill',
|
|
246
|
+
manifest: content,
|
|
247
|
+
});
|
|
176
248
|
}
|
|
177
249
|
else {
|
|
178
250
|
// YAML skill file
|
|
179
251
|
const parsed = safeParseYAML(content);
|
|
180
252
|
const meta = parsed?.metadata;
|
|
181
253
|
skills.push({
|
|
182
|
-
name: meta?.name ||
|
|
254
|
+
name: meta?.name ||
|
|
255
|
+
path.basename(file, path.extname(file)).replace('.skill', ''),
|
|
183
256
|
source: 'local',
|
|
184
|
-
description: meta?.description ||
|
|
257
|
+
description: meta?.description ||
|
|
258
|
+
parsed?.description ||
|
|
259
|
+
'Local skill',
|
|
185
260
|
version: meta?.version || undefined,
|
|
186
|
-
capabilities: Array.isArray(parsed?.capabilities)
|
|
187
|
-
|
|
261
|
+
capabilities: Array.isArray(parsed?.capabilities)
|
|
262
|
+
? parsed.capabilities
|
|
263
|
+
: undefined,
|
|
264
|
+
tags: Array.isArray(parsed?.tags)
|
|
265
|
+
? parsed.tags
|
|
266
|
+
: undefined,
|
|
188
267
|
manifest: parsed,
|
|
189
268
|
});
|
|
190
269
|
}
|
|
@@ -201,7 +280,10 @@ let SkillAggregatorService = class SkillAggregatorService {
|
|
|
201
280
|
async loadGitHub() {
|
|
202
281
|
const url = 'https://api.github.com/repos/anthropics/skills/contents/';
|
|
203
282
|
const response = await fetch(url, {
|
|
204
|
-
headers: {
|
|
283
|
+
headers: {
|
|
284
|
+
Accept: 'application/vnd.github.v3+json',
|
|
285
|
+
'User-Agent': 'ossa-daemon',
|
|
286
|
+
},
|
|
205
287
|
signal: AbortSignal.timeout(10_000),
|
|
206
288
|
});
|
|
207
289
|
if (!response.ok) {
|
|
@@ -239,7 +321,9 @@ let SkillAggregatorService = class SkillAggregatorService {
|
|
|
239
321
|
source: 'registry',
|
|
240
322
|
description: item.description || 'Registry skill',
|
|
241
323
|
version: item.version || undefined,
|
|
242
|
-
capabilities: Array.isArray(item.capabilities)
|
|
324
|
+
capabilities: Array.isArray(item.capabilities)
|
|
325
|
+
? item.capabilities
|
|
326
|
+
: undefined,
|
|
243
327
|
tags: Array.isArray(item.tags) ? item.tags : undefined,
|
|
244
328
|
}));
|
|
245
329
|
}
|
|
@@ -23,7 +23,7 @@ import { PairingService } from './pairing.service.js';
|
|
|
23
23
|
const SSE_HEADERS = {
|
|
24
24
|
'Content-Type': 'text/event-stream',
|
|
25
25
|
'Cache-Control': 'no-cache',
|
|
26
|
-
|
|
26
|
+
Connection: 'keep-alive',
|
|
27
27
|
};
|
|
28
28
|
const STATUS_INTERVAL_MS = 5_000;
|
|
29
29
|
let SSEEndpoints = class SSEEndpoints {
|
|
@@ -34,7 +34,9 @@ let DaemonWebSocketServer = class DaemonWebSocketServer {
|
|
|
34
34
|
server.on('upgrade', (request, socket, head) => {
|
|
35
35
|
// Enforce localhost-only binding at the connection level
|
|
36
36
|
const remoteAddr = request.socket.remoteAddress;
|
|
37
|
-
if (remoteAddr !== '127.0.0.1' &&
|
|
37
|
+
if (remoteAddr !== '127.0.0.1' &&
|
|
38
|
+
remoteAddr !== '::1' &&
|
|
39
|
+
remoteAddr !== '::ffff:127.0.0.1') {
|
|
38
40
|
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
39
41
|
socket.destroy();
|
|
40
42
|
return;
|
|
@@ -42,7 +44,10 @@ let DaemonWebSocketServer = class DaemonWebSocketServer {
|
|
|
42
44
|
// Extract pairing token from Sec-WebSocket-Protocol header
|
|
43
45
|
const protocols = request.headers['sec-websocket-protocol'];
|
|
44
46
|
const token = typeof protocols === 'string'
|
|
45
|
-
? protocols
|
|
47
|
+
? protocols
|
|
48
|
+
.split(',')
|
|
49
|
+
.map((p) => p.trim())
|
|
50
|
+
.find((p) => p.startsWith('pairing.'))
|
|
46
51
|
: undefined;
|
|
47
52
|
if (!token) {
|
|
48
53
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
@@ -71,7 +76,9 @@ let DaemonWebSocketServer = class DaemonWebSocketServer {
|
|
|
71
76
|
*/
|
|
72
77
|
sendToSession(sessionId, event) {
|
|
73
78
|
const session = this.sessions.get(sessionId);
|
|
74
|
-
if (session &&
|
|
79
|
+
if (session &&
|
|
80
|
+
session.authenticated &&
|
|
81
|
+
session.ws.readyState === WebSocket.OPEN) {
|
|
75
82
|
session.ws.send(JSON.stringify(event));
|
|
76
83
|
}
|
|
77
84
|
}
|
|
@@ -36,7 +36,7 @@ export class CedarGovernanceProvider {
|
|
|
36
36
|
return {
|
|
37
37
|
compliant: issues.length === 0,
|
|
38
38
|
issues,
|
|
39
|
-
warnings
|
|
39
|
+
warnings,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
@@ -51,7 +51,8 @@ export class CedarGovernanceProvider {
|
|
|
51
51
|
const policies_evaluated = [];
|
|
52
52
|
// Simulate Policy Evaluation Context
|
|
53
53
|
const principalTier = context?.tier || 'tier_1_read';
|
|
54
|
-
const isDestructive = action.toLowerCase().includes('delete') ||
|
|
54
|
+
const isDestructive = action.toLowerCase().includes('delete') ||
|
|
55
|
+
action.toLowerCase().includes('write');
|
|
55
56
|
policies_evaluated.push('policy_tier_bounds');
|
|
56
57
|
// Sample Cedar constraint translation
|
|
57
58
|
if (principalTier === 'tier_1_read' && isDestructive) {
|
|
@@ -62,14 +63,15 @@ export class CedarGovernanceProvider {
|
|
|
62
63
|
// In a real integration, this would call the Cedar C++ binding or WASM module:
|
|
63
64
|
// const cedarEval = await cedar.isAuthorized({ principal, action, resource, context });
|
|
64
65
|
decision = 'ALLOW';
|
|
65
|
-
reason =
|
|
66
|
+
reason =
|
|
67
|
+
'Allowed by default test policy matching principal and action scope.';
|
|
66
68
|
}
|
|
67
69
|
return {
|
|
68
70
|
decision,
|
|
69
71
|
reason,
|
|
70
72
|
diagnostics: {
|
|
71
73
|
policies_evaluated,
|
|
72
|
-
}
|
|
74
|
+
},
|
|
73
75
|
};
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
@@ -78,7 +80,7 @@ export class CedarGovernanceProvider {
|
|
|
78
80
|
async evaluateQualityGate(request) {
|
|
79
81
|
const blocked_by = [];
|
|
80
82
|
if (request.environment === 'production') {
|
|
81
|
-
if (request.metrics.confidence_score < 0.
|
|
83
|
+
if (request.metrics.confidence_score < 0.9) {
|
|
82
84
|
blocked_by.push('Confidence score below production threshold (90%)');
|
|
83
85
|
}
|
|
84
86
|
if (request.metrics.vulnerability_count > 0) {
|
|
@@ -87,7 +89,7 @@ export class CedarGovernanceProvider {
|
|
|
87
89
|
}
|
|
88
90
|
else {
|
|
89
91
|
// lower environments
|
|
90
|
-
if (request.metrics.confidence_score < 0.
|
|
92
|
+
if (request.metrics.confidence_score < 0.7) {
|
|
91
93
|
blocked_by.push('Confidence score below development threshold (70%)');
|
|
92
94
|
}
|
|
93
95
|
}
|
|
@@ -97,8 +99,10 @@ export class CedarGovernanceProvider {
|
|
|
97
99
|
blocked_by,
|
|
98
100
|
policy_decision: {
|
|
99
101
|
decision: decision === 'PASS' ? 'ALLOW' : 'DENY',
|
|
100
|
-
reason: decision === 'PASS'
|
|
101
|
-
|
|
102
|
+
reason: decision === 'PASS'
|
|
103
|
+
? 'Quality gate matched constraints.'
|
|
104
|
+
: 'Metrics failed quality requirements.',
|
|
105
|
+
},
|
|
102
106
|
};
|
|
103
107
|
}
|
|
104
108
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This is OSSA's responsibility: schema definition + offline validation.
|
|
6
6
|
* Runtime enforcement belongs to DUADP.
|
|
7
7
|
*/
|
|
8
|
-
import { checkParsePolicySet, checkParseSchema, } from '@cedar-policy/cedar-wasm';
|
|
8
|
+
import { checkParsePolicySet, checkParseSchema, } from '@cedar-policy/cedar-wasm/nodejs';
|
|
9
9
|
function extractErrors(answer) {
|
|
10
10
|
if (answer.type === 'failure') {
|
|
11
11
|
return answer.errors.map((e) => e.message ?? JSON.stringify(e));
|