Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ tasks:
npm run test

# Run SDK example to verify it works
#cd {{ .ROOT_DIR }}/sdk/examples/example-js
#npm install
#npm run example
cd {{ .ROOT_DIR }}/examples
npm install
npm run example

sdk:release:javascript:
desc: Release javascript client SDK package
Expand Down
3 changes: 3 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
*.tsbuildinfo
144 changes: 144 additions & 0 deletions examples/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright AGNTCY Contributors (https://github.com/agntcy)
// SPDX-License-Identifier: Apache-2.0

import { Client, Config, models } from "agntcy-dir";

function generateRecords(names) {
return names.map((name) =>
JSON.parse(`
{
"data": {
"name": "${name}",
"version": "v1.0.0",
"schema_version": "0.8.0",
"description": "My example agent",
"authors": ["AGNTCY"],
"created_at": "2025-03-19T17:06:37Z",
"skills": [
{
"name": "natural_language_processing/natural_language_generation/text_completion",
"id": 10201
},
{
"name": "natural_language_processing/analytical_reasoning/problem_solving",
"id": 10702
}
],
"locators": [
{
"type": "docker_image",
"url": "https://ghcr.io/agntcy/marketing-strategy"
}
],
"domains": [
{
"name": "technology/networking",
"id": 103
}
],
"modules": [
{
"name": "integration/a2a",
"id": 203,
"data": {
"protocol_version": "lightweight orchestra moral",
"card_data": "centres",
"capabilities": [
"state_transition_history",
"push_notifications"
],
"transports": [
"grpc",
"http"
],
"output_modes": [
"text/html"
]
}
}
]
}
}
`),
);
}

(async () => {
// Create client
const config = Config.loadFromEnv();
let t = await Client.createGRPCTransport(config);
const client = new Client(config, t);

// Create record objects
const records = generateRecords(["example-record", "example-record2"]);

// Push objects
const pushed_refs = await client.push(records);
pushed_refs.forEach((ref) => {
console.log("Pushed object ref:", ref);
});

// Pull objects
const pulled_records = await client.pull(pushed_refs);
pulled_records.forEach((pulled_record) => {
console.log("Pulled object:", pulled_record);
});

// Lookup objects
const metadatas = await client.lookup(pushed_refs);
metadatas.forEach((metadata) => {
console.log("Lookup result:", metadata);
});

// Search objects
const search_response = await client.searchCIDs({
queries: [
{
type: models.search_v1.RecordQueryType.SKILL_ID,
value: "10201",
},
],
limit: 3,
});
console.log("Search result:", search_response);

// Publish objects
await client.publish({
request: {
case: "recordRefs",
value: {
refs: pushed_refs,
},
},
});
console.log("Objects published.");

// List objects in the routing table
const list_response = await client.list({
queries: [
{
type: models.routing_v1.RecordQueryType.SKILL,
value:
"natural_language_processing/analytical_reasoning/problem_solving",
},
],
});
list_response.forEach((r) => {
console.log("Listed objects:", r);
});

// Unpublish objects
await client.unpublish({
request: {
case: "recordRefs",
value: {
refs: pushed_refs,
},
},
});
console.log("Objects unpublished.");

// Delete objects
await client.delete(pushed_refs);
console.log("Objects deleted.");
})();
141 changes: 141 additions & 0 deletions examples/example_interactive_oidc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright AGNTCY Contributors (https://github.com/agntcy)
// SPDX-License-Identifier: Apache-2.0

/**
* Interactive OIDC example: runs SearchCIDs only (parity with example_interactive_oidc.py).
*
* Requires DIRECTORY_CLIENT_OIDC_CLIENT_ID. Optional: DIRECTORY_CLIENT_OIDC_CLIENT_SECRET,
* DIRECTORY_CLIENT_SERVER_ADDRESS, DIRECTORY_CLIENT_TLS_SERVER_NAME, DIRECTORY_CLIENT_OIDC_REDIRECT_URI,
* DIRECTORY_CLIENT_OIDC_CALLBACK_PORT, DIRECTORY_CLIENT_OIDC_AUTH_TIMEOUT, DIRECTORY_CLIENT_AUTH_TOKEN,
* DIRECTORY_CLIENT_TLS_SKIP_VERIFY.
*/

import { Client, Config, OAuthPkceError, TokenCache, models } from "agntcy-dir";

const DEFAULT_OIDC_ISSUER = "https://dev.idp.ads.outshift.io";
const DEFAULT_SERVER_ADDRESS = "dev.gateway.ads.outshift.io:443";
const DEFAULT_TLS_SERVER_NAME = "dev.gateway.ads.outshift.io";
const DEFAULT_REDIRECT_URI = "http://localhost:8484/callback";

function requireEnv(name) {
const value = (process.env[name] ?? "").trim();
if (!value) {
throw new Error(`${name} is required for the interactive OIDC example`);
}
return value;
}

/** Same truthy strings as Config.loadFromEnv (DIRECTORY_CLIENT_TLS_SKIP_VERIFY). */
function parseBoolEnv(value, defaultVal = false) {
if (value === undefined || value === "") {
return defaultVal;
}
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
}

function parseArgs(argv) {
const out = { version: "v1*", limit: 3 };
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (a === "--version" && argv[i + 1]) {
out.version = argv[++i];
} else if (a === "--limit" && argv[i + 1]) {
const n = Number.parseInt(argv[++i], 10);
if (Number.isFinite(n)) {
out.limit = n;
}
}
}
return out;
}

function hasUsableOidcTokenWithoutPkce() {
const authToken = (process.env.DIRECTORY_CLIENT_AUTH_TOKEN ?? "").trim();
if (authToken) {
return true;
}
return new TokenCache().getValidToken() !== undefined;
}

function parseOidcCallbackPort() {
const raw = process.env.DIRECTORY_CLIENT_OIDC_CALLBACK_PORT;
if (raw === undefined || raw === "") {
return Config.DEFAULT_OIDC_CALLBACK_PORT;
}
const n = Number.parseInt(raw, 10);
return Number.isFinite(n) ? n : Config.DEFAULT_OIDC_CALLBACK_PORT;
}

function parseOidcAuthTimeout() {
const raw = process.env.DIRECTORY_CLIENT_OIDC_AUTH_TIMEOUT;
if (raw === undefined || raw === "") {
return Config.DEFAULT_OIDC_AUTH_TIMEOUT;
}
const n = Number.parseFloat(raw);
return Number.isFinite(n) ? n : Config.DEFAULT_OIDC_AUTH_TIMEOUT;
}

function buildConfig() {
return new Config(
process.env.DIRECTORY_CLIENT_SERVER_ADDRESS ?? DEFAULT_SERVER_ADDRESS,
Config.DEFAULT_DIRCTL_PATH,
Config.DEFAULT_SPIFFE_ENDPOINT_SOCKET,
"oidc",
Config.DEFAULT_JWT_AUDIENCE,
Config.DEFAULT_TLS_CA_FILE,
Config.DEFAULT_TLS_CERT_FILE,
Config.DEFAULT_TLS_KEY_FILE,
(process.env.DIRECTORY_CLIENT_AUTH_TOKEN ?? "").trim(),
process.env.DIRECTORY_CLIENT_TLS_SERVER_NAME ?? DEFAULT_TLS_SERVER_NAME,
parseBoolEnv(process.env.DIRECTORY_CLIENT_TLS_SKIP_VERIFY, false),
DEFAULT_OIDC_ISSUER,
requireEnv("DIRECTORY_CLIENT_OIDC_CLIENT_ID"),
process.env.DIRECTORY_CLIENT_OIDC_CLIENT_SECRET ?? "",
process.env.DIRECTORY_CLIENT_OIDC_REDIRECT_URI ?? DEFAULT_REDIRECT_URI,
parseOidcCallbackPort(),
parseOidcAuthTimeout(),
undefined,
undefined,
);
}

async function buildClient() {
const config = buildConfig();
const client = new Client(config);

if (hasUsableOidcTokenWithoutPkce()) {
console.log("Using cached OIDC token.");
return client;
}

console.log("No cached OIDC token found. Starting interactive login.");
await client.authenticateOAuthPkce();
return client;
}

(async () => {
try {
const args = parseArgs(process.argv);
const client = await buildClient();

const objects = await client.searchCIDs({
queries: [
{
type: models.search_v1.RecordQueryType.VERSION,
value: args.version,
},
],
limit: args.limit,
});

console.log(`SearchCIDs results for version ${JSON.stringify(args.version)}:`);
for (const obj of objects) {
console.log(obj);
}
} catch (e) {
if (e instanceof OAuthPkceError) {
console.error(`Interactive OIDC login failed: ${e.message}`);
}
throw e;
}
})();
87 changes: 87 additions & 0 deletions examples/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading