Skip to content

Commit 598836b

Browse files
feat(dir-sdk-javascript): add examples
Signed-off-by: Bendegúz Csirmaz <csirmazbendeguz@gmail.com>
1 parent a0c827e commit 598836b

6 files changed

Lines changed: 390 additions & 3 deletions

File tree

Taskfile.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,6 @@ tasks:
8888
npm run test
8989
9090
# Run SDK example to verify it works
91-
#cd {{ .ROOT_DIR }}/sdk/examples/example-js
92-
#npm install
93-
#npm run example
91+
cd {{ .ROOT_DIR }}/examples
92+
npm install
93+
npm run example

examples/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
*.tsbuildinfo

examples/example.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright AGNTCY Contributors (https://github.com/agntcy)
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Client, Config, models } from "agntcy-dir";
5+
6+
function generateRecords(names) {
7+
return names.map((name) =>
8+
JSON.parse(`
9+
{
10+
"data": {
11+
"name": "${name}",
12+
"version": "v1.0.0",
13+
"schema_version": "0.8.0",
14+
"description": "My example agent",
15+
"authors": ["AGNTCY"],
16+
"created_at": "2025-03-19T17:06:37Z",
17+
"skills": [
18+
{
19+
"name": "natural_language_processing/natural_language_generation/text_completion",
20+
"id": 10201
21+
},
22+
{
23+
"name": "natural_language_processing/analytical_reasoning/problem_solving",
24+
"id": 10702
25+
}
26+
],
27+
"locators": [
28+
{
29+
"type": "docker_image",
30+
"url": "https://ghcr.io/agntcy/marketing-strategy"
31+
}
32+
],
33+
"domains": [
34+
{
35+
"name": "technology/networking",
36+
"id": 103
37+
}
38+
],
39+
"modules": [
40+
{
41+
"name": "integration/a2a",
42+
"id": 203,
43+
"data": {
44+
"protocol_version": "lightweight orchestra moral",
45+
"card_data": "centres",
46+
"capabilities": [
47+
"state_transition_history",
48+
"push_notifications"
49+
],
50+
"transports": [
51+
"grpc",
52+
"http"
53+
],
54+
"output_modes": [
55+
"text/html"
56+
]
57+
}
58+
}
59+
]
60+
}
61+
}
62+
`),
63+
);
64+
}
65+
66+
(async () => {
67+
// Create client
68+
const config = Config.loadFromEnv();
69+
let t = await Client.createGRPCTransport(config);
70+
const client = new Client(config, t);
71+
72+
// Create record objects
73+
const records = generateRecords(["example-record", "example-record2"]);
74+
75+
// Push objects
76+
const pushed_refs = await client.push(records);
77+
pushed_refs.forEach((ref) => {
78+
console.log("Pushed object ref:", ref);
79+
});
80+
81+
// Pull objects
82+
const pulled_records = await client.pull(pushed_refs);
83+
pulled_records.forEach((pulled_record) => {
84+
console.log("Pulled object:", pulled_record);
85+
});
86+
87+
// Lookup objects
88+
const metadatas = await client.lookup(pushed_refs);
89+
metadatas.forEach((metadata) => {
90+
console.log("Lookup result:", metadata);
91+
});
92+
93+
// Search objects
94+
const search_response = await client.searchCIDs({
95+
queries: [
96+
{
97+
type: models.search_v1.RecordQueryType.SKILL_ID,
98+
value: "10201",
99+
},
100+
],
101+
limit: 3,
102+
});
103+
console.log("Search result:", search_response);
104+
105+
// Publish objects
106+
await client.publish({
107+
request: {
108+
case: "recordRefs",
109+
value: {
110+
refs: pushed_refs,
111+
},
112+
},
113+
});
114+
console.log("Objects published.");
115+
116+
// List objects in the routing table
117+
const list_response = await client.list({
118+
queries: [
119+
{
120+
type: models.routing_v1.RecordQueryType.SKILL,
121+
value:
122+
"natural_language_processing/analytical_reasoning/problem_solving",
123+
},
124+
],
125+
});
126+
list_response.forEach((r) => {
127+
console.log("Listed objects:", r);
128+
});
129+
130+
// Unpublish objects
131+
await client.unpublish({
132+
request: {
133+
case: "recordRefs",
134+
value: {
135+
refs: pushed_refs,
136+
},
137+
},
138+
});
139+
console.log("Objects unpublished.");
140+
141+
// Delete objects
142+
await client.delete(pushed_refs);
143+
console.log("Objects deleted.");
144+
})();
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright AGNTCY Contributors (https://github.com/agntcy)
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/**
5+
* Interactive OIDC example: runs SearchCIDs only (parity with example_interactive_oidc.py).
6+
*
7+
* Requires DIRECTORY_CLIENT_OIDC_CLIENT_ID. Optional: DIRECTORY_CLIENT_OIDC_CLIENT_SECRET,
8+
* DIRECTORY_CLIENT_SERVER_ADDRESS, DIRECTORY_CLIENT_TLS_SERVER_NAME, DIRECTORY_CLIENT_OIDC_REDIRECT_URI,
9+
* DIRECTORY_CLIENT_OIDC_CALLBACK_PORT, DIRECTORY_CLIENT_OIDC_AUTH_TIMEOUT, DIRECTORY_CLIENT_AUTH_TOKEN,
10+
* DIRECTORY_CLIENT_TLS_SKIP_VERIFY.
11+
*/
12+
13+
import { Client, Config, OAuthPkceError, TokenCache, models } from "agntcy-dir";
14+
15+
const DEFAULT_OIDC_ISSUER = "https://dev.idp.ads.outshift.io";
16+
const DEFAULT_SERVER_ADDRESS = "dev.gateway.ads.outshift.io:443";
17+
const DEFAULT_TLS_SERVER_NAME = "dev.gateway.ads.outshift.io";
18+
const DEFAULT_REDIRECT_URI = "http://localhost:8484/callback";
19+
20+
function requireEnv(name) {
21+
const value = (process.env[name] ?? "").trim();
22+
if (!value) {
23+
throw new Error(`${name} is required for the interactive OIDC example`);
24+
}
25+
return value;
26+
}
27+
28+
/** Same truthy strings as Config.loadFromEnv (DIRECTORY_CLIENT_TLS_SKIP_VERIFY). */
29+
function parseBoolEnv(value, defaultVal = false) {
30+
if (value === undefined || value === "") {
31+
return defaultVal;
32+
}
33+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
34+
}
35+
36+
function parseArgs(argv) {
37+
const out = { version: "v1*", limit: 3 };
38+
for (let i = 2; i < argv.length; i++) {
39+
const a = argv[i];
40+
if (a === "--version" && argv[i + 1]) {
41+
out.version = argv[++i];
42+
} else if (a === "--limit" && argv[i + 1]) {
43+
const n = Number.parseInt(argv[++i], 10);
44+
if (Number.isFinite(n)) {
45+
out.limit = n;
46+
}
47+
}
48+
}
49+
return out;
50+
}
51+
52+
function hasUsableOidcTokenWithoutPkce() {
53+
const authToken = (process.env.DIRECTORY_CLIENT_AUTH_TOKEN ?? "").trim();
54+
if (authToken) {
55+
return true;
56+
}
57+
return new TokenCache().getValidToken() !== undefined;
58+
}
59+
60+
function parseOidcCallbackPort() {
61+
const raw = process.env.DIRECTORY_CLIENT_OIDC_CALLBACK_PORT;
62+
if (raw === undefined || raw === "") {
63+
return Config.DEFAULT_OIDC_CALLBACK_PORT;
64+
}
65+
const n = Number.parseInt(raw, 10);
66+
return Number.isFinite(n) ? n : Config.DEFAULT_OIDC_CALLBACK_PORT;
67+
}
68+
69+
function parseOidcAuthTimeout() {
70+
const raw = process.env.DIRECTORY_CLIENT_OIDC_AUTH_TIMEOUT;
71+
if (raw === undefined || raw === "") {
72+
return Config.DEFAULT_OIDC_AUTH_TIMEOUT;
73+
}
74+
const n = Number.parseFloat(raw);
75+
return Number.isFinite(n) ? n : Config.DEFAULT_OIDC_AUTH_TIMEOUT;
76+
}
77+
78+
function buildConfig() {
79+
return new Config(
80+
process.env.DIRECTORY_CLIENT_SERVER_ADDRESS ?? DEFAULT_SERVER_ADDRESS,
81+
Config.DEFAULT_DIRCTL_PATH,
82+
Config.DEFAULT_SPIFFE_ENDPOINT_SOCKET,
83+
"oidc",
84+
Config.DEFAULT_JWT_AUDIENCE,
85+
Config.DEFAULT_TLS_CA_FILE,
86+
Config.DEFAULT_TLS_CERT_FILE,
87+
Config.DEFAULT_TLS_KEY_FILE,
88+
(process.env.DIRECTORY_CLIENT_AUTH_TOKEN ?? "").trim(),
89+
process.env.DIRECTORY_CLIENT_TLS_SERVER_NAME ?? DEFAULT_TLS_SERVER_NAME,
90+
parseBoolEnv(process.env.DIRECTORY_CLIENT_TLS_SKIP_VERIFY, false),
91+
DEFAULT_OIDC_ISSUER,
92+
requireEnv("DIRECTORY_CLIENT_OIDC_CLIENT_ID"),
93+
process.env.DIRECTORY_CLIENT_OIDC_CLIENT_SECRET ?? "",
94+
process.env.DIRECTORY_CLIENT_OIDC_REDIRECT_URI ?? DEFAULT_REDIRECT_URI,
95+
parseOidcCallbackPort(),
96+
parseOidcAuthTimeout(),
97+
undefined,
98+
undefined,
99+
);
100+
}
101+
102+
async function buildClient() {
103+
const config = buildConfig();
104+
const client = new Client(config);
105+
106+
if (hasUsableOidcTokenWithoutPkce()) {
107+
console.log("Using cached OIDC token.");
108+
return client;
109+
}
110+
111+
console.log("No cached OIDC token found. Starting interactive login.");
112+
await client.authenticateOAuthPkce();
113+
return client;
114+
}
115+
116+
(async () => {
117+
try {
118+
const args = parseArgs(process.argv);
119+
const client = await buildClient();
120+
121+
const objects = await client.searchCIDs({
122+
queries: [
123+
{
124+
type: models.search_v1.RecordQueryType.VERSION,
125+
value: args.version,
126+
},
127+
],
128+
limit: args.limit,
129+
});
130+
131+
console.log(`SearchCIDs results for version ${JSON.stringify(args.version)}:`);
132+
for (const obj of objects) {
133+
console.log(obj);
134+
}
135+
} catch (e) {
136+
if (e instanceof OAuthPkceError) {
137+
console.error(`Interactive OIDC login failed: ${e.message}`);
138+
}
139+
throw e;
140+
}
141+
})();

examples/package-lock.json

Lines changed: 87 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)