logicsrc/plugins/credential-sharing/src/index.ts
Anthony Ettinger cf475f0f4c
Some checks are pending
CI / build (push) Waiting to run
test / test (push) Waiting to run
fix(cli): point logicsrc login at the real app + add device-code login
`logicsrc login` defaulted to http://localhost:4010 — a dev origin that
doesn't exist on an installed machine, so the printed authorize URL went
nowhere. It now defaults to the hosted credentials app (apps/pwa), reads
the documented $LOGICSRC_API, and only reuses a stored apiUrl once that
identity has actually completed a login (which is how machines got stuck
pointing at localhost). Note logicsrc.com is the marketing site and has
no /cli routes.

The loopback flow is also unusable over SSH: redirect_uri is
http://127.0.0.1:<port>/callback, which resolves to the *browser's*
machine, not the CLI's. Added a device-authorization flow — the CLI
prints a short user_code, the human approves it from any browser:

  POST /cli/device/code   mint device_code + user_code (10 min TTL)
  GET  /cli/device        approve page (login required; typo-tolerant)
  POST /cli/device        approve/deny (CSRF-guarded browser form)
  POST /cli/device/token  CLI polls -> lsk_ API key

device_code is stored sha256-hashed, single-use, with authorization_pending
/ slow_down / access_denied / expired_token poll semantics. The CLI picks
the flow automatically (SSH/CI/no-DISPLAY -> device), with --device/--web
to force it and a fallback to loopback against servers without /cli/device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:07:49 +00:00

104 lines
3.3 KiB
TypeScript

import type { PluginDefinition } from "@logicsrc/plugin-core";
import { credentialSharingManifest } from "./manifest.js";
import { CredentialEngine, type CredentialEngineOptions } from "./engine.js";
import { listCredentialProviderManifests } from "./providers/index.js";
export const credentialSharingPlugin: PluginDefinition = {
manifest: credentialSharingManifest,
configDefaults: {
enabled: true,
default_policy: "approval_required_for_destructive",
credential_home: "${LOGICSRC_CREDENTIAL_HOME}"
},
routes: [
{ method: "GET", path: "/api/credentials/providers", capability: "credentials.providers.list" },
{ method: "GET", path: "/api/credentials/inspect", capability: "credentials.inspect" },
{ method: "POST", path: "/api/credentials/diff", capability: "credentials.diff" },
{ method: "POST", path: "/api/credentials/plans", capability: "credentials.plan" },
{ method: "POST", path: "/api/credentials/plans/:id/approve", capability: "credentials.approve" },
{ method: "POST", path: "/api/credentials/plans/:id/sync", capability: "credentials.sync" },
{ method: "POST", path: "/api/credentials/runs/:id/rollback", capability: "credentials.rollback" },
{ method: "GET", path: "/api/credentials/runs/:id/audit", capability: "credentials.audit.read" }
],
permissions: [
"credentials:inspect",
"credentials:diff",
"credentials:plan",
"credentials:approve",
"credentials:sync",
"credentials:rollback",
"credentials:audit:read"
],
tuiPanels: [{ id: "credential-sharing", title: "Credential Sharing" }]
};
/** Factory mirroring the LogicSRC Credential Sharing SDK spec. */
export function createCredentialEngine(options: CredentialEngineOptions = {}): CredentialEngine {
return new CredentialEngine(options);
}
/** Provider listing without constructing an engine (used by the CLI `providers` command). */
export function listCredentialProviders() {
return listCredentialProviderManifests();
}
export { credentialSharingManifest };
export { CredentialEngine, DEFAULT_CREDENTIAL_POLICY, endpointLabel } from "./engine.js";
export type { CredentialEngineOptions } from "./engine.js";
export {
credentialProviders,
credentialProviderRegistry,
listCredentialProviderManifests,
envProvider,
dopplerProvider,
railwayProvider,
githubSecretsProvider,
teamProvider,
parseEnv,
applyEnv
} from "./providers/index.js";
export {
TeamClient,
TeamApiError,
type TeamClientOptions,
type RemoteUser,
type RemoteTeam,
type RemoteMember,
type RemoteVault,
type RemoteSecret,
type RemoteGrantRow
} from "./client.js";
export {
generateIdentityKeyPair,
generateVaultKey,
wrapVaultKey,
unwrapVaultKey,
encryptValue,
decryptValue,
publicKeyForSecret,
type IdentityKeyPair,
type SealedValue
} from "./crypto.js";
export {
loadOrCreateIdentity,
readIdentity,
saveIdentity,
updateIdentity,
requireAuth,
verifyIdentityIntegrity,
identityPath,
logicsrcHome,
defaultApiUrl,
envApiUrl,
resolveApiUrl,
DEFAULT_API_URL,
type LocalIdentity
} from "./identity.js";
export {
createFileCredentialStore,
createMemoryCredentialStore,
defaultCredentialHome,
type CredentialStore
} from "./store.js";
export { fingerprintValue, fingerprintsEqual } from "./fingerprint.js";
export * from "./types.js";