The Paperclip runtime card's Local CLI tab previously only derived an apiUrl
from the local config and then made HTTP calls itself, so the Test action and
the company/agent pickers ignored the user's onboarded CLI auth context.
Add CLI-backed variants for every Paperclip call that has a `paperclipai`
counterpart, and route through them when transport=cli — both from the
settings card and from the runtime adapter's prompt path.
- New plugin functions spawning `paperclipai … --json`:
* probePaperclipViaCli, listCompaniesViaCli, listCompanyAgentsViaCli
(settings card test + pickers)
* createIssueViaCli, getIssueViaCli, agentsMeViaCli (runtime hot path)
- New dashboard routes: /providers/paperclip/cli-status, /cli-companies,
/cli-agents (read-only façades over the plugin's CLI helpers)
- PaperclipRuntimeCard: branches on transport=cli to use the cli-* fetchers
- PaperclipRuntimeAdapter: stores transport on the session and routes
createIssue/getIssue + identity derivation through CLI variants in CLI mode;
raises a clear error when agentId is unset in CLI mode (paperclipai has no
/agents/me equivalent)
- getIssueComments / wakeAgent / getRunEvents stay on HTTP (no matching
paperclipai subcommands) and continue to use the apiKey discovered from
the local paperclipai config, so CLI mode still works end-to-end
- Tests: 9 new paperclip-client tests covering each CLI variant + 5 new
adapter tests for the Local-CLI transport branch (64/64 plugin tests pass)
- Update routes.test for the existing BUNDLED_PLUGIN_RUNTIMES fallback so
the bundled hermes/openclaw/paperclip entries are expected alongside
installed plugins
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import { definePlugin } from "@fusion/plugin-sdk";
|
|
import {
|
|
probePaperclipConnection,
|
|
resolvePaperclipConfig,
|
|
} from "./paperclip-client.js";
|
|
import { PaperclipRuntimeAdapter } from "./runtime-adapter.js";
|
|
import type {
|
|
FusionPlugin,
|
|
PluginRuntimeRegistration,
|
|
RuntimeLogger,
|
|
} from "./types.js";
|
|
|
|
// Public exports — consumed by the dashboard probe façade and tests.
|
|
export type {
|
|
PaperclipAgentSummary,
|
|
PaperclipCliDiscovery,
|
|
PaperclipCliDiscoveryResult,
|
|
PaperclipCompanySummary,
|
|
PaperclipConnectionStatus,
|
|
} from "./paperclip-client.js";
|
|
export {
|
|
agentsMe,
|
|
agentsMeViaCli,
|
|
createIssueViaCli,
|
|
discoverPaperclipCliConfig,
|
|
getIssueViaCli,
|
|
listCompanies,
|
|
listCompaniesViaCli,
|
|
listCompanyAgents,
|
|
listCompanyAgentsViaCli,
|
|
mintAgentApiKeyViaCli,
|
|
probePaperclipConnection,
|
|
probePaperclipViaCli,
|
|
} from "./paperclip-client.js";
|
|
export type { MintCliKeyOptions, MintedApiKey } from "./paperclip-client.js";
|
|
export { PaperclipRuntimeAdapter } from "./runtime-adapter.js";
|
|
|
|
function getSettingsConfig(settings: unknown) {
|
|
return resolvePaperclipConfig((settings ?? {}) as Record<string, unknown>);
|
|
}
|
|
|
|
async function paperclipRuntimeFactory(ctx: {
|
|
settings?: unknown;
|
|
logger?: RuntimeLogger;
|
|
}): Promise<unknown> {
|
|
const config = getSettingsConfig(ctx.settings);
|
|
// resolvePaperclipConfig returns `mode: string`; the adapter narrows it.
|
|
return new PaperclipRuntimeAdapter(
|
|
config as unknown as Record<string, unknown>,
|
|
ctx.logger,
|
|
);
|
|
}
|
|
|
|
const paperclipRuntime: PluginRuntimeRegistration = {
|
|
metadata: {
|
|
runtimeId: "paperclip",
|
|
name: "Paperclip Runtime",
|
|
description: "Drives a Paperclip agent via the wakeup + heartbeat-run REST API",
|
|
version: "1.0.0",
|
|
},
|
|
factory: paperclipRuntimeFactory,
|
|
};
|
|
|
|
const plugin: FusionPlugin = definePlugin({
|
|
manifest: {
|
|
id: "fusion-plugin-paperclip-runtime",
|
|
name: "Paperclip Runtime Plugin",
|
|
version: "1.0.0",
|
|
description: "Drives a Paperclip agent via the wakeup + heartbeat-run REST API",
|
|
author: "Fusion Team",
|
|
homepage: "https://paperclip.ing/",
|
|
fusionVersion: ">=0.1.0",
|
|
runtime: {
|
|
runtimeId: "paperclip",
|
|
name: "Paperclip Runtime",
|
|
description: "Drives a Paperclip agent via the wakeup + heartbeat-run REST API",
|
|
version: "1.0.0",
|
|
},
|
|
},
|
|
state: "installed",
|
|
runtime: paperclipRuntime,
|
|
hooks: {
|
|
onLoad: async (ctx) => {
|
|
const config = getSettingsConfig(ctx.settings);
|
|
ctx.logger.info(`Paperclip Runtime Plugin loaded (apiUrl=${config.apiUrl})`);
|
|
|
|
// Best-effort connectivity probe; failures are warnings, not errors.
|
|
try {
|
|
const status = await probePaperclipConnection({
|
|
apiUrl: config.apiUrl,
|
|
apiKey: config.apiKey,
|
|
});
|
|
if (status.available) {
|
|
const ident = status.identity;
|
|
ctx.logger.info(
|
|
ident
|
|
? `Paperclip reachable as ${ident.agentName} (${ident.role ?? "agent"}) at ${ident.companyName ?? ident.companyId}`
|
|
: `Paperclip reachable at ${config.apiUrl}`,
|
|
);
|
|
} else {
|
|
ctx.logger.warn(`Paperclip probe failed: ${status.reason ?? "unknown"}`);
|
|
}
|
|
} catch (error) {
|
|
const reason = error instanceof Error ? error.message : String(error);
|
|
ctx.logger.warn(`Paperclip probe threw: ${reason}`);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
export default plugin;
|