Merge master; OpenFleet keeps its name, PR 177's rental descriptor becomes OpenRental

PR 177 shipped a second OpenFleet under the same slug: a published listing of OpenAgent profiles and ipfile swarms with CoinPay rental offers. Anthony ruled that OpenFleet means the human-controlled fleet and the record of who spawned whom, so the rental contract is renamed OpenRental (slug openrental, catalogs family, group noun listing): docs/openrental.md, logicsrc-openrental.schema.json with type logicsrc.openrental and scope kind listing, fixtures/openrental, createOpenRental and OpenRental* types in the SDK, the openrental validator kind. Minor bumps because an export moved: @logicsrc/schemas 0.2.0, @logicsrc/validators 0.2.0, @logicsrc/sdk 0.2.0. The discovery contract test now covers openfleet, openrental and openwall, one per family, and accepts a landing-page link in llms.txt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
This commit is contained in:
Anthony Ettinger 2026-09-13 08:45:33 +00:00
commit 68f3151699
35 changed files with 2068 additions and 14 deletions

34
packages/sdk/README.md Normal file
View file

@ -0,0 +1,34 @@
# @logicsrc/sdk
LogicSRC contract types and constructors for tasks, AgentSwarm sessions and
OpenRental descriptors. These helpers construct documents; they do not start
agents, resolve remote identities or initiate payments.
```sh
npm install @logicsrc/sdk @logicsrc/validators
```
```ts
import { createOpenRental } from "@logicsrc/sdk";
import { validate } from "@logicsrc/validators";
const listing = createOpenRental({
id: "https://example.com/listings/research",
name: "Research",
owner_did: "operator.coinpay",
members: [{
kind: "openagent",
id: "analyst.coinpay",
url: "https://example.com/agents/analyst.json"
}]
});
const result = validate("openrental", listing);
if (!result.ok) throw new Error(JSON.stringify(result.errors));
```
Use `@logicsrc/validators` 0.2.0 or newer for OpenRental validation. See the
[OpenRental specification](https://logicsrc.com/docs/openrental) for member
bindings, exact decimal rental rates and CoinPay settlement metadata.
MIT © Profullstack, Inc.

View file

@ -1,6 +1,6 @@
{
"name": "@logicsrc/sdk",
"version": "0.1.0",
"version": "0.2.0",
"description": "LogicSRC SDK contract types and client interface.",
"type": "module",
"main": "./dist/index.js",
@ -11,5 +11,19 @@
},
"devDependencies": {
"vitest": "^4.0.8"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/profullstack/logicsrc.git",
"directory": "packages/sdk"
},
"homepage": "https://github.com/profullstack/logicsrc/tree/master/packages/sdk",
"files": [
"dist",
"!dist/*.test.*"
],
"publishConfig": {
"access": "public"
}
}

View file

@ -1,3 +1,9 @@
export { createOpenRental } from "./openrental.js";
export type {
OpenRental, OpenRentalJsonValue, OpenRentalMember, OpenRentalMemberReference,
OpenRentalRental, OpenRentalRentalScope
} from "./openrental.js";
export type LogicSrcId = string;
export interface LogicSrcTask {

View file

@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import { createOpenRental, type OpenRentalMember } from "./index.js";
const members: OpenRentalMember[] = [
{ kind: "openagent", id: "analyst.coinpay", url: "https://example.com/agent.json" },
{ kind: "openswarm", id: `ed25519:${"a".repeat(64)}`, url: "https://example.com/manifest.json" }
];
describe("createOpenRental", () => {
it.each([members.slice(0, 1), members.slice(1), members])("constructs single-kind and mixed listings", (...fleetMembers) => {
const listing = createOpenRental({
id: "https://example.com/listings/research", name: "Research", owner_did: "operator.coinpay",
members: fleetMembers
});
expect(listing.type).toBe("logicsrc.openrental");
expect(listing.version).toBe("0.1");
expect(listing.members).toEqual(fleetMembers);
expect(listing.rentals).toBeUndefined();
});
it("keeps rental amounts as decimal strings and preserves metadata", () => {
const input: Parameters<typeof createOpenRental>[0] = {
id: "https://example.com/listings/research", name: "Research", owner_did: "operator.coinpay", members,
metadata: { support: { languages: ["en", "fr"] } },
rentals: [{
id: "hourly", scope: { kind: "listing" },
rate: { amount: "9007199254740993.123456", currency: "USD", unit: "hour" },
payment: { provider: "coinpay", payee_did: "operator.coinpay", checkout_url: "https://example.com/rent" }
}]
};
const snapshot = structuredClone(input);
expect(createOpenRental(input)).toEqual({ ...input, type: "logicsrc.openrental", version: "0.1" });
expect(input).toEqual(snapshot);
});
});

View file

@ -0,0 +1,71 @@
export type OpenRentalJsonValue =
| null | boolean | number | string
| OpenRentalJsonValue[] | { [key: string]: OpenRentalJsonValue };
/** OpenAgent binds to a logicsrc.agent DID; OpenSwarm binds to a file key. */
export type OpenRentalMemberReference =
| { kind: "openagent"; id: string }
| { kind: "openswarm"; id: `ed25519:${string}` };
export type OpenRentalMember = OpenRentalMemberReference & {
/** HTTPS URL of the agent profile or the signed ipfile manifest. */
url: string;
name?: string;
role?: string;
metadata?: Record<string, OpenRentalJsonValue>;
};
export type OpenRentalRentalScope =
| { kind: "listing" }
| { kind: "members"; members: OpenRentalMemberReference[] };
export interface OpenRentalRental {
id: string;
name?: string;
scope: OpenRentalRentalScope;
rate: {
/** Exact non-negative USD decimal string, with six fractional digits. */
amount: string;
currency: "USD";
/** Whole billing units; a month is 30 days, a task an agreed deliverable. */
unit: "hour" | "day" | "month" | "task";
};
minimum_units?: number;
maximum_units?: number;
valid_from?: string;
valid_until?: string;
terms_url?: string;
payment: {
provider: "coinpay";
payee_did: string;
/** Merchant-published HTTPS checkout entry point; no fixed CoinPay API. */
checkout_url: string;
};
metadata?: Record<string, OpenRentalJsonValue>;
}
export interface OpenRental {
type: "logicsrc.openrental";
version: "0.1";
/** Stable HTTPS listing identifier, unique at its publisher's origin. */
id: string;
name: string;
owner_did: string;
description?: string;
updated_at?: string;
availability?: "available" | "busy" | "offline" | "unknown";
tags?: string[];
capabilities?: string[];
metadata?: Record<string, OpenRentalJsonValue>;
members: OpenRentalMember[];
rentals?: OpenRentalRental[];
}
/**
* Construct a descriptor without fetching members or initiating payments.
* Use validate("openrental", document) from @logicsrc/validators before use:
* TypeScript alone cannot check DID/key formats or rental references.
*/
export function createOpenRental(input: Omit<OpenRental, "type" | "version">): OpenRental {
return { ...input, type: "logicsrc.openrental", version: "0.1" };
}