@pikku/cli 0.12.39 → 0.12.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console-app/assets/{index-Dxl3JsMK.js → index-D9Z9rySK.js} +2 -2
- package/console-app/index.html +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +3 -3
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.d.ts +6 -6
- package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +47 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +2 -2
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +229 -178
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +5 -2
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +13 -9
- package/dist/.pikku/schemas/schemas/LogoutInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/WhoamiInput.schema.json +1 -0
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +45 -0
- package/dist/src/deploy/build-pipeline.d.ts +1 -0
- package/dist/src/deploy/build-pipeline.js +1 -1
- package/dist/src/fabric/fabric-commands.d.ts +63 -63
- package/dist/src/fabric/functions/db-schema.function.d.ts +3 -3
- package/dist/src/fabric/functions/deploy-list.function.d.ts +3 -3
- package/dist/src/fabric/functions/deploy-units.function.d.ts +3 -3
- package/dist/src/fabric/functions/deploy.function.d.ts +6 -6
- package/dist/src/fabric/functions/domains-add.function.d.ts +3 -3
- package/dist/src/fabric/functions/domains-list.function.d.ts +3 -3
- package/dist/src/fabric/functions/domains-remove.function.d.ts +3 -3
- package/dist/src/fabric/functions/errors.function.d.ts +3 -3
- package/dist/src/fabric/functions/init.function.d.ts +3 -3
- package/dist/src/fabric/functions/link.function.d.ts +3 -3
- package/dist/src/fabric/functions/llm-key.function.d.ts +3 -3
- package/dist/src/fabric/functions/login.function.d.ts +3 -3
- package/dist/src/fabric/functions/logs.function.d.ts +3 -3
- package/dist/src/fabric/functions/metrics.function.d.ts +3 -3
- package/dist/src/fabric/functions/rollback.function.d.ts +3 -3
- package/dist/src/fabric/functions/secrets-list.function.d.ts +3 -3
- package/dist/src/fabric/functions/secrets-set.function.d.ts +3 -3
- package/dist/src/fabric/functions/status.function.d.ts +3 -3
- package/dist/src/fabric/functions/trace.function.d.ts +3 -3
- package/dist/src/fabric/functions/validate-core.js +2 -7
- package/dist/src/fabric/functions/validate.function.d.ts +3 -3
- package/dist/src/fabric/functions/validate.function.js +16 -14
- package/dist/src/functions/commands/all.d.ts +1 -1
- package/dist/src/functions/commands/binary.d.ts +3 -3
- package/dist/src/functions/commands/bootstrap.d.ts +1 -1
- package/dist/src/functions/commands/console.d.ts +3 -3
- package/dist/src/functions/commands/db-audit.d.ts +1 -1
- package/dist/src/functions/commands/db-generate.d.ts +1 -1
- package/dist/src/functions/commands/db-generate.js +0 -3
- package/dist/src/functions/commands/db-migrate.d.ts +1 -1
- package/dist/src/functions/commands/db-reset.d.ts +1 -1
- package/dist/src/functions/commands/db-reset.js +11 -7
- package/dist/src/functions/commands/db-seed.d.ts +1 -1
- package/dist/src/functions/commands/db-seed.js +4 -7
- package/dist/src/functions/commands/db-shared.js +2 -4
- package/dist/src/functions/commands/deploy-apply.d.ts +3 -3
- package/dist/src/functions/commands/deploy-apply.js +1 -0
- package/dist/src/functions/commands/deploy-info.d.ts +1 -1
- package/dist/src/functions/commands/deploy-plan.d.ts +3 -3
- package/dist/src/functions/commands/deploy-plan.js +1 -0
- package/dist/src/functions/commands/dev.d.ts +3 -3
- package/dist/src/functions/commands/dev.js +1 -1
- package/dist/src/functions/commands/emails-init.d.ts +1 -1
- package/dist/src/functions/commands/enable.d.ts +5 -5
- package/dist/src/functions/commands/info.d.ts +4 -4
- package/dist/src/functions/commands/login.d.ts +37 -0
- package/dist/src/functions/commands/login.js +85 -0
- package/dist/src/functions/commands/meta.d.ts +31 -31
- package/dist/src/functions/commands/new-addon.d.ts +3 -3
- package/dist/src/functions/commands/new-function.d.ts +3 -3
- package/dist/src/functions/commands/new-middleware.d.ts +3 -3
- package/dist/src/functions/commands/new-permission.d.ts +3 -3
- package/dist/src/functions/commands/new-wiring.d.ts +3 -3
- package/dist/src/functions/commands/pikku-command-bootstrap.d.ts +1 -1
- package/dist/src/functions/commands/pikku-command-summary.d.ts +1 -1
- package/dist/src/functions/commands/skills.d.ts +6 -6
- package/dist/src/functions/commands/tests-coverage.d.ts +3 -3
- package/dist/src/functions/commands/tests-init.d.ts +3 -3
- package/dist/src/functions/commands/versions-check.d.ts +1 -1
- package/dist/src/functions/commands/versions-init.d.ts +3 -3
- package/dist/src/functions/commands/versions-update.d.ts +1 -1
- package/dist/src/functions/commands/watch.d.ts +3 -3
- package/dist/src/functions/commands/workspace-validate.d.ts +3 -3
- package/dist/src/functions/db/local-db.d.ts +9 -5
- package/dist/src/functions/db/local-db.js +275 -107
- package/dist/src/functions/db/postgres/pglite-kysely.d.ts +8 -0
- package/dist/src/functions/db/postgres/pglite-kysely.js +79 -0
- package/dist/src/functions/db/postgres/postgres-introspector.d.ts +1 -0
- package/dist/src/functions/db/postgres/postgres-introspector.js +6 -1
- package/dist/src/functions/db/postgres/postgres-migrator.d.ts +7 -2
- package/dist/src/functions/db/postgres/postgres-migrator.js +6 -1
- package/dist/src/functions/runtimes/fetch/index.d.ts +1 -1
- package/dist/src/functions/runtimes/nextjs/pikku-command-nextjs.d.ts +1 -1
- package/dist/src/functions/runtimes/tanstack-start/pikku-command-tanstack-start.d.ts +1 -1
- package/dist/src/functions/runtimes/websocket/pikku-command-websocket-typed.d.ts +1 -1
- package/dist/src/functions/validate/workspace-validate.js +4 -4
- package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent-types.d.ts +1 -1
- package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent.d.ts +1 -1
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.d.ts +1 -1
- package/dist/src/functions/wirings/auth/pikku-command-auth.d.ts +1 -1
- package/dist/src/functions/wirings/channels/pikku-channels.d.ts +1 -1
- package/dist/src/functions/wirings/channels/pikku-command-channel-types.d.ts +1 -1
- package/dist/src/functions/wirings/channels/pikku-command-channels-map.d.ts +1 -1
- package/dist/src/functions/wirings/channels/pikku-command-channels.d.ts +1 -1
- package/dist/src/functions/wirings/cli/pikku-command-cli-entry.d.ts +1 -1
- package/dist/src/functions/wirings/cli/pikku-command-cli-types.d.ts +1 -1
- package/dist/src/functions/wirings/cli/pikku-command-cli.d.ts +1 -1
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +38 -7
- package/dist/src/functions/wirings/console/pikku-command-console-functions.d.ts +1 -1
- package/dist/src/functions/wirings/console/pikku-command-node-types.d.ts +1 -1
- package/dist/src/functions/wirings/console/pikku-command-nodes-meta.d.ts +1 -1
- package/dist/src/functions/wirings/credentials/pikku-command-credentials.d.ts +1 -1
- package/dist/src/functions/wirings/emails/pikku-command-emails.d.ts +1 -1
- package/dist/src/functions/wirings/functions/pikku-command-addon-types.d.ts +1 -1
- package/dist/src/functions/wirings/functions/pikku-command-function-types-split.d.ts +3 -3
- package/dist/src/functions/wirings/functions/pikku-command-function-types.d.ts +3 -3
- package/dist/src/functions/wirings/functions/pikku-command-functions.d.ts +1 -1
- package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
- package/dist/src/functions/wirings/functions/schemas.d.ts +1 -1
- package/dist/src/functions/wirings/gateway/pikku-command-gateway.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-command-http-map.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-command-http-routes.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-command-http-types.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-command-openapi.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-http-routes.d.ts +1 -1
- package/dist/src/functions/wirings/mcp/pikku-command-mcp-json.d.ts +1 -1
- package/dist/src/functions/wirings/mcp/pikku-command-mcp-types.d.ts +1 -1
- package/dist/src/functions/wirings/mcp/pikku-command-mcp.d.ts +1 -1
- package/dist/src/functions/wirings/middleware/pikku-command-middleware.d.ts +1 -1
- package/dist/src/functions/wirings/package/pikku-command-package-types.d.ts +2 -2
- package/dist/src/functions/wirings/package/pikku-command-package.d.ts +1 -1
- package/dist/src/functions/wirings/permissions/pikku-command-permissions.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue-map.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue-service.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue-types.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-queue-map.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-queue.d.ts +1 -1
- package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.d.ts +1 -1
- package/dist/src/functions/wirings/realtime/pikku-command-realtime.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-react-query.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc-client.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc-map.d.ts +2 -2
- package/dist/src/functions/wirings/rpc/pikku-command-rpc.d.ts +1 -1
- package/dist/src/functions/wirings/scheduler/pikku-command-scheduler-types.d.ts +1 -1
- package/dist/src/functions/wirings/scheduler/pikku-command-scheduler.d.ts +1 -1
- package/dist/src/functions/wirings/secrets/pikku-command-secrets.d.ts +1 -1
- package/dist/src/functions/wirings/triggers/pikku-command-trigger-types.d.ts +3 -3
- package/dist/src/functions/wirings/triggers/pikku-command-trigger.d.ts +1 -1
- package/dist/src/functions/wirings/variables/pikku-command-variables.d.ts +1 -1
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.d.ts +1 -1
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.d.ts +1 -1
- package/dist/src/scaffold/rpc-remote.gen.d.ts +3 -3
- package/dist/src/scaffold/rpc-remote.gen.js +1 -1
- package/dist/src/utils/cli-session.d.ts +40 -0
- package/dist/src/utils/cli-session.js +73 -0
- package/dist/src/utils/device-auth.d.ts +28 -0
- package/dist/src/utils/device-auth.js +111 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/skills/pikku-machine-auth/SKILL.md +177 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A locally-persisted CLI session obtained via `pikku login` (the better-auth
|
|
3
|
+
* device-authorization flow). Stored at `~/.pikku/session.json`, keyed by the
|
|
4
|
+
* server's base URL so a single user can be logged into multiple pikku servers.
|
|
5
|
+
*
|
|
6
|
+
* `accessToken` is a better-auth session token — present it as
|
|
7
|
+
* `Authorization: Bearer <accessToken>` (requires the `bearer` plugin on the
|
|
8
|
+
* server). This is the HUMAN path; machine agents use scoped API keys
|
|
9
|
+
* (`x-api-key`) instead and never read this file.
|
|
10
|
+
*/
|
|
11
|
+
export interface PikkuCliSession {
|
|
12
|
+
baseURL: string;
|
|
13
|
+
accessToken: string;
|
|
14
|
+
tokenType: string;
|
|
15
|
+
/** ISO timestamp; absent when the server did not report an expiry. */
|
|
16
|
+
expiresAt?: string;
|
|
17
|
+
user?: {
|
|
18
|
+
id: string;
|
|
19
|
+
email?: string | null;
|
|
20
|
+
name?: string | null;
|
|
21
|
+
};
|
|
22
|
+
obtainedAt: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const sessionFilePath: () => string;
|
|
25
|
+
/** Drop a trailing slash so `https://x/` and `https://x` share one entry. */
|
|
26
|
+
export declare const normalizeBaseURL: (url: string) => string;
|
|
27
|
+
/** Persist a session and mark it the current default target. */
|
|
28
|
+
export declare const saveSession: (session: PikkuCliSession) => Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Load a stored session. With no `baseURL`, returns the current default. Returns
|
|
31
|
+
* `null` if none is stored.
|
|
32
|
+
*/
|
|
33
|
+
export declare const loadSession: (baseURL?: string) => Promise<PikkuCliSession | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Remove a stored session (the given one, or the current default). Returns the
|
|
36
|
+
* base URL that was removed, or `null` if nothing matched.
|
|
37
|
+
*/
|
|
38
|
+
export declare const clearSession: (baseURL?: string) => Promise<string | null>;
|
|
39
|
+
/** True when the session has a known expiry that is in the past. */
|
|
40
|
+
export declare const isSessionExpired: (session: PikkuCliSession, nowMs?: number) => boolean;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { readFile, writeFile, mkdir, rm } from 'node:fs/promises';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
const SESSION_DIR = join(homedir(), '.pikku');
|
|
6
|
+
const SESSION_FILE = join(SESSION_DIR, 'session.json');
|
|
7
|
+
export const sessionFilePath = () => SESSION_FILE;
|
|
8
|
+
/** Drop a trailing slash so `https://x/` and `https://x` share one entry. */
|
|
9
|
+
export const normalizeBaseURL = (url) => url.trim().replace(/\/+$/, '');
|
|
10
|
+
const readSessionFile = async () => {
|
|
11
|
+
if (!existsSync(SESSION_FILE)) {
|
|
12
|
+
return { sessions: {} };
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(await readFile(SESSION_FILE, 'utf-8'));
|
|
16
|
+
return { sessions: parsed.sessions ?? {}, current: parsed.current };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Corrupt file — treat as empty rather than crashing every command.
|
|
20
|
+
return { sessions: {} };
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const writeSessionFile = async (file) => {
|
|
24
|
+
await mkdir(dirname(SESSION_FILE), { recursive: true });
|
|
25
|
+
// 0600 — the file holds bearer tokens.
|
|
26
|
+
await writeFile(SESSION_FILE, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
27
|
+
};
|
|
28
|
+
/** Persist a session and mark it the current default target. */
|
|
29
|
+
export const saveSession = async (session) => {
|
|
30
|
+
const file = await readSessionFile();
|
|
31
|
+
const key = normalizeBaseURL(session.baseURL);
|
|
32
|
+
file.sessions[key] = { ...session, baseURL: key };
|
|
33
|
+
file.current = key;
|
|
34
|
+
await writeSessionFile(file);
|
|
35
|
+
return SESSION_FILE;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Load a stored session. With no `baseURL`, returns the current default. Returns
|
|
39
|
+
* `null` if none is stored.
|
|
40
|
+
*/
|
|
41
|
+
export const loadSession = async (baseURL) => {
|
|
42
|
+
const file = await readSessionFile();
|
|
43
|
+
const key = baseURL ? normalizeBaseURL(baseURL) : file.current;
|
|
44
|
+
if (!key) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return file.sessions[key] ?? null;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Remove a stored session (the given one, or the current default). Returns the
|
|
51
|
+
* base URL that was removed, or `null` if nothing matched.
|
|
52
|
+
*/
|
|
53
|
+
export const clearSession = async (baseURL) => {
|
|
54
|
+
const file = await readSessionFile();
|
|
55
|
+
const key = baseURL ? normalizeBaseURL(baseURL) : file.current;
|
|
56
|
+
if (!key || !file.sessions[key]) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
delete file.sessions[key];
|
|
60
|
+
if (file.current === key) {
|
|
61
|
+
file.current = Object.keys(file.sessions)[0];
|
|
62
|
+
}
|
|
63
|
+
if (Object.keys(file.sessions).length === 0) {
|
|
64
|
+
// Nothing left — remove the file entirely rather than leaving an empty husk.
|
|
65
|
+
await rm(SESSION_FILE, { force: true });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await writeSessionFile(file);
|
|
69
|
+
}
|
|
70
|
+
return key;
|
|
71
|
+
};
|
|
72
|
+
/** True when the session has a known expiry that is in the past. */
|
|
73
|
+
export const isSessionExpired = (session, nowMs = Date.now()) => session.expiresAt ? new Date(session.expiresAt).getTime() <= nowMs : false;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PikkuCliSession } from './cli-session.js';
|
|
2
|
+
export interface DevicePrompt {
|
|
3
|
+
/** Where the user approves the request. */
|
|
4
|
+
verificationUri: string;
|
|
5
|
+
/** Same as above with the code pre-filled (open this if present). */
|
|
6
|
+
verificationUriComplete?: string;
|
|
7
|
+
userCode: string;
|
|
8
|
+
/** Absolute ms timestamp when the code expires. */
|
|
9
|
+
expiresAtMs: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DeviceLoginOptions {
|
|
12
|
+
/** Server origin, e.g. `https://app.example.com`. */
|
|
13
|
+
baseURL: string;
|
|
14
|
+
/** better-auth basePath. Defaults to `/auth`. */
|
|
15
|
+
authBasePath?: string;
|
|
16
|
+
/** Logical client identifier (no OIDC registration required). */
|
|
17
|
+
clientId?: string;
|
|
18
|
+
/** Optional space-separated scopes to request. */
|
|
19
|
+
scope?: string;
|
|
20
|
+
/** Called once the device code is issued, so the caller can prompt the user. */
|
|
21
|
+
onPrompt: (prompt: DevicePrompt) => void | Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run the better-auth device-authorization flow against `baseURL` and return a
|
|
25
|
+
* persisted-shaped session (token + best-effort expiry/user). The human
|
|
26
|
+
* approves in the browser; this only requests a code and polls for the token.
|
|
27
|
+
*/
|
|
28
|
+
export declare const deviceLogin: (opts: DeviceLoginOptions) => Promise<PikkuCliSession>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { normalizeBaseURL } from './cli-session.js';
|
|
2
|
+
/** RFC 8628 device-authorization grant type. */
|
|
3
|
+
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
4
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
5
|
+
const joinUrl = (base, path) => `${base}${path.startsWith('/') ? '' : '/'}${path}`;
|
|
6
|
+
/**
|
|
7
|
+
* Run the better-auth device-authorization flow against `baseURL` and return a
|
|
8
|
+
* persisted-shaped session (token + best-effort expiry/user). The human
|
|
9
|
+
* approves in the browser; this only requests a code and polls for the token.
|
|
10
|
+
*/
|
|
11
|
+
export const deviceLogin = async (opts) => {
|
|
12
|
+
const baseURL = normalizeBaseURL(opts.baseURL);
|
|
13
|
+
const authBase = `${baseURL}${opts.authBasePath ?? '/auth'}`;
|
|
14
|
+
const clientId = opts.clientId ?? 'pikku-cli';
|
|
15
|
+
// 1. Request a device + user code.
|
|
16
|
+
const codeRes = await fetch(joinUrl(authBase, '/device/code'), {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: { 'content-type': 'application/json' },
|
|
19
|
+
body: JSON.stringify({ client_id: clientId, scope: opts.scope }),
|
|
20
|
+
});
|
|
21
|
+
if (!codeRes.ok) {
|
|
22
|
+
throw new Error(`device/code failed (${codeRes.status}): ${await codeRes.text()}`);
|
|
23
|
+
}
|
|
24
|
+
const code = (await codeRes.json());
|
|
25
|
+
const expiresAtMs = Date.now() + code.expires_in * 1000;
|
|
26
|
+
await opts.onPrompt({
|
|
27
|
+
verificationUri: code.verification_uri,
|
|
28
|
+
verificationUriComplete: code.verification_uri_complete,
|
|
29
|
+
userCode: code.user_code,
|
|
30
|
+
expiresAtMs,
|
|
31
|
+
});
|
|
32
|
+
// 2. Poll the token endpoint until approved, denied, or expired.
|
|
33
|
+
let intervalMs = Math.max(1, code.interval) * 1000;
|
|
34
|
+
while (Date.now() < expiresAtMs) {
|
|
35
|
+
await sleep(intervalMs);
|
|
36
|
+
const tokenRes = await fetch(joinUrl(authBase, '/device/token'), {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'content-type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
grant_type: DEVICE_GRANT_TYPE,
|
|
41
|
+
device_code: code.device_code,
|
|
42
|
+
client_id: clientId,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
const body = (await tokenRes.json().catch(() => ({})));
|
|
46
|
+
if (tokenRes.ok && 'access_token' in body && body.access_token) {
|
|
47
|
+
return finalizeSession(baseURL, authBase, body);
|
|
48
|
+
}
|
|
49
|
+
const error = body.error;
|
|
50
|
+
if (error === 'authorization_pending') {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (error === 'slow_down') {
|
|
54
|
+
// Back off as the spec requires.
|
|
55
|
+
intervalMs += 5000;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (error === 'access_denied') {
|
|
59
|
+
throw new Error('Login was denied in the browser.');
|
|
60
|
+
}
|
|
61
|
+
if (error === 'expired_token') {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
// Unknown non-pending error — surface it rather than spinning.
|
|
65
|
+
throw new Error(`device/token failed (${tokenRes.status}): ${JSON.stringify(body)}`);
|
|
66
|
+
}
|
|
67
|
+
throw new Error('Login timed out before it was approved.');
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Turn a token response into a stored session. Best-effort enriches it with the
|
|
71
|
+
* server-reported expiry + user by calling `/auth/get-session` with the bearer
|
|
72
|
+
* token (works when the `bearer` plugin is enabled); falls back to the token's
|
|
73
|
+
* own `expires_in` if that call is unavailable.
|
|
74
|
+
*/
|
|
75
|
+
const finalizeSession = async (baseURL, authBase, token) => {
|
|
76
|
+
const obtainedAt = new Date().toISOString();
|
|
77
|
+
let expiresAt;
|
|
78
|
+
let user;
|
|
79
|
+
if (typeof token.expires_in === 'number') {
|
|
80
|
+
expiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const res = await fetch(joinUrl(authBase, '/get-session'), {
|
|
84
|
+
headers: { authorization: `Bearer ${token.access_token}` },
|
|
85
|
+
});
|
|
86
|
+
if (res.ok) {
|
|
87
|
+
const data = (await res.json());
|
|
88
|
+
if (data?.session?.expiresAt) {
|
|
89
|
+
expiresAt = new Date(data.session.expiresAt).toISOString();
|
|
90
|
+
}
|
|
91
|
+
if (data?.user) {
|
|
92
|
+
user = {
|
|
93
|
+
id: data.user.id,
|
|
94
|
+
email: data.user.email,
|
|
95
|
+
name: data.user.name,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// get-session not reachable — keep whatever expiry we already have.
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
baseURL,
|
|
105
|
+
accessToken: token.access_token,
|
|
106
|
+
tokenType: token.token_type ?? 'Bearer',
|
|
107
|
+
expiresAt,
|
|
108
|
+
user,
|
|
109
|
+
obtainedAt,
|
|
110
|
+
};
|
|
111
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../bin/pikku.ts","../src/cli.wiring.ts","../src/services.ts","../src/deploy/build-pipeline.ts","../src/deploy/provider-adapter.ts","../src/deploy/server-entry.ts","../src/deploy/analyzer/analyzer.ts","../src/deploy/analyzer/index.ts","../src/deploy/analyzer/manifest.ts","../src/deploy/bundler/bundler.ts","../src/deploy/bundler/dep-extractor.ts","../src/deploy/bundler/index.ts","../src/deploy/bundler/types.ts","../src/deploy/codegen/index.ts","../src/deploy/codegen/per-unit-codegen.ts","../src/deploy/plan/executor.ts","../src/deploy/plan/formatter.ts","../src/deploy/plan/index.ts","../src/deploy/plan/planner.ts","../src/deploy/plan/provider.ts","../src/deploy/plan/types.ts","../src/fabric/fabric-commands.ts","../src/fabric/functions/db-schema.function.ts","../src/fabric/functions/deploy-list.function.ts","../src/fabric/functions/deploy-units.function.ts","../src/fabric/functions/deploy.function.ts","../src/fabric/functions/domains-add.function.ts","../src/fabric/functions/domains-list.function.ts","../src/fabric/functions/domains-remove.function.ts","../src/fabric/functions/errors.function.ts","../src/fabric/functions/init.function.ts","../src/fabric/functions/link.function.ts","../src/fabric/functions/llm-key.function.ts","../src/fabric/functions/login.function.ts","../src/fabric/functions/logs.function.ts","../src/fabric/functions/metrics.function.ts","../src/fabric/functions/rollback.function.ts","../src/fabric/functions/secrets-list.function.ts","../src/fabric/functions/secrets-set.function.ts","../src/fabric/functions/status.function.ts","../src/fabric/functions/trace.function.ts","../src/fabric/functions/validate-core.ts","../src/fabric/functions/validate.function.ts","../src/fabric/lib/config.ts","../src/fabric/lib/console-url.ts","../src/fabric/lib/git.ts","../src/fabric/lib/http.ts","../src/fabric/lib/not-implemented.ts","../src/fabric/lib/output.ts","../src/fabric/lib/prompt.ts","../src/fabric/lib/stage.ts","../src/fabric/sdk/http-map.gen.d.ts","../src/fabric/sdk/pikku-fetch.gen.ts","../src/fabric/sdk/pikku-rpc.gen.ts","../src/fabric/sdk/rpc-map.gen.d.ts","../src/functions/commands/all.ts","../src/functions/commands/binary.ts","../src/functions/commands/bootstrap.ts","../src/functions/commands/console.ts","../src/functions/commands/db-audit.ts","../src/functions/commands/db-generate.ts","../src/functions/commands/db-migrate.ts","../src/functions/commands/db-reset.ts","../src/functions/commands/db-seed.ts","../src/functions/commands/db-shared.ts","../src/functions/commands/deploy-apply.ts","../src/functions/commands/deploy-info.ts","../src/functions/commands/deploy-plan.ts","../src/functions/commands/dev.ts","../src/functions/commands/emails-init.ts","../src/functions/commands/enable.ts","../src/functions/commands/info.ts","../src/functions/commands/load-user-project.ts","../src/functions/commands/meta.ts","../src/functions/commands/new-addon.ts","../src/functions/commands/new-function.ts","../src/functions/commands/new-middleware.ts","../src/functions/commands/new-permission.ts","../src/functions/commands/new-wiring.ts","../src/functions/commands/pikku-command-bootstrap.ts","../src/functions/commands/pikku-command-summary.ts","../src/functions/commands/skills.ts","../src/functions/commands/tests-coverage.ts","../src/functions/commands/tests-init.ts","../src/functions/commands/versions-check.ts","../src/functions/commands/versions-init.ts","../src/functions/commands/versions-update.ts","../src/functions/commands/watch.ts","../src/functions/commands/workspace-validate.ts","../src/functions/db/annotation-parser.ts","../src/functions/db/better-auth-schema.ts","../src/functions/db/coercion-plugin.ts","../src/functions/db/db-codegen.ts","../src/functions/db/db-introspector.ts","../src/functions/db/db-migrator.ts","../src/functions/db/local-db.ts","../src/functions/db/zod-codegen.ts","../src/functions/db/postgres/postgres-introspector.ts","../src/functions/db/postgres/postgres-migrator.ts","../src/functions/db/sqlite/seed.ts","../src/functions/db/sqlite/sqlite-introspector.ts","../src/functions/db/sqlite/sqlite-kysely.ts","../src/functions/db/sqlite/sqlite-migrator.ts","../src/functions/db/sqlite/sqlite-runtime-bun.ts","../src/functions/db/sqlite/sqlite-runtime-node.ts","../src/functions/db/sqlite/sqlite-runtime.ts","../src/functions/runtimes/fetch/index.ts","../src/functions/runtimes/nextjs/pikku-command-nextjs.ts","../src/functions/runtimes/nextjs/serialize-nextjs-backend-worker-rpc-wrapper.ts","../src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/functions/runtimes/nextjs/serialize-nextjs-http-wrapper.ts","../src/functions/runtimes/tanstack-start/pikku-command-tanstack-start.ts","../src/functions/runtimes/tanstack-start/serialize-tanstack-start-shim.ts","../src/functions/runtimes/websocket/pikku-command-websocket-typed.ts","../src/functions/runtimes/websocket/serialize-websocket-wrapper.ts","../src/functions/validate/workspace-validate.ts","../src/functions/wirings/ai-agent/pikku-command-ai-agent-types.ts","../src/functions/wirings/ai-agent/pikku-command-ai-agent.ts","../src/functions/wirings/ai-agent/pikku-command-public-agent.ts","../src/functions/wirings/ai-agent/serialize-agent-map.ts","../src/functions/wirings/ai-agent/serialize-ai-agent-types.ts","../src/functions/wirings/ai-agent/serialize-public-agent.ts","../src/functions/wirings/auth/pikku-command-auth.ts","../src/functions/wirings/auth/serialize-auth-gen.ts","../src/functions/wirings/auth/serialize-auth-meta.ts","../src/functions/wirings/auth/serialize-auth-types.ts","../src/functions/wirings/channels/pikku-channels.ts","../src/functions/wirings/channels/pikku-command-channel-types.ts","../src/functions/wirings/channels/pikku-command-channels-map.ts","../src/functions/wirings/channels/pikku-command-channels.ts","../src/functions/wirings/channels/serialize-channel-types.ts","../src/functions/wirings/channels/serialize-typed-channel-map.ts","../src/functions/wirings/cli/pikku-command-cli-entry.ts","../src/functions/wirings/cli/pikku-command-cli-types.ts","../src/functions/wirings/cli/pikku-command-cli.ts","../src/functions/wirings/cli/serialize-channel-cli-client.ts","../src/functions/wirings/cli/serialize-channel-cli.ts","../src/functions/wirings/cli/serialize-cli-types.ts","../src/functions/wirings/cli/serialize-local-cli-bootstrap.ts","../src/functions/wirings/console/pikku-command-console-functions.ts","../src/functions/wirings/console/pikku-command-node-types.ts","../src/functions/wirings/console/pikku-command-nodes-meta.ts","../src/functions/wirings/console/serialize-console-functions.ts","../src/functions/wirings/console/serialize-node-types.ts","../src/functions/wirings/credentials/pikku-command-credentials.ts","../src/functions/wirings/credentials/serialize-credentials-types.ts","../src/functions/wirings/emails/pikku-command-emails.ts","../src/functions/wirings/emails/serialize-emails.ts","../src/functions/wirings/functions/pikku-command-addon-types.ts","../src/functions/wirings/functions/pikku-command-function-types-split.ts","../src/functions/wirings/functions/pikku-command-function-types.ts","../src/functions/wirings/functions/pikku-command-functions.ts","../src/functions/wirings/functions/pikku-command-services.ts","../src/functions/wirings/functions/schemas.ts","../src/functions/wirings/functions/serialize-addon-types.ts","../src/functions/wirings/functions/serialize-function-imports.ts","../src/functions/wirings/functions/serialize-function-types.ts","../src/functions/wirings/functions/serialize-pikku-types-hub.ts","../src/functions/wirings/gateway/pikku-command-gateway.ts","../src/functions/wirings/http/pikku-command-http-map.ts","../src/functions/wirings/http/pikku-command-http-routes.ts","../src/functions/wirings/http/pikku-command-http-types.ts","../src/functions/wirings/http/pikku-command-openapi.ts","../src/functions/wirings/http/pikku-http-routes.ts","../src/functions/wirings/http/serialize-fetch-wrapper.ts","../src/functions/wirings/http/serialize-http-types.ts","../src/functions/wirings/http/serialize-typed-http-map.ts","../src/functions/wirings/mcp/pikku-command-mcp-json.ts","../src/functions/wirings/mcp/pikku-command-mcp-types.ts","../src/functions/wirings/mcp/pikku-command-mcp.ts","../src/functions/wirings/mcp/serialize-mcp-types.ts","../src/functions/wirings/middleware/pikku-command-middleware.ts","../src/functions/wirings/middleware/serialize-middleware-imports.ts","../src/functions/wirings/package/pikku-command-package-types.ts","../src/functions/wirings/package/pikku-command-package.ts","../src/functions/wirings/package/serialize-package-types.ts","../src/functions/wirings/package/serialize-package.ts","../src/functions/wirings/permissions/pikku-command-permissions.ts","../src/functions/wirings/permissions/serialize-permissions-imports.ts","../src/functions/wirings/queue/pikku-command-queue-map.ts","../src/functions/wirings/queue/pikku-command-queue-service.ts","../src/functions/wirings/queue/pikku-command-queue-types.ts","../src/functions/wirings/queue/pikku-command-queue.ts","../src/functions/wirings/queue/pikku-queue-map.ts","../src/functions/wirings/queue/pikku-queue.ts","../src/functions/wirings/queue/serialize-queue-map.ts","../src/functions/wirings/queue/serialize-queue-meta.ts","../src/functions/wirings/queue/serialize-queue-types.ts","../src/functions/wirings/queue/serialize-queue-wrapper.ts","../src/functions/wirings/realtime/pikku-command-events-scaffold.ts","../src/functions/wirings/realtime/pikku-command-realtime.ts","../src/functions/wirings/realtime/serialize-events-scaffold.ts","../src/functions/wirings/realtime/serialize-realtime-client.ts","../src/functions/wirings/rpc/pikku-command-public-rpc.ts","../src/functions/wirings/rpc/pikku-command-react-query.ts","../src/functions/wirings/rpc/pikku-command-remote-rpc.ts","../src/functions/wirings/rpc/pikku-command-rpc-client.ts","../src/functions/wirings/rpc/pikku-command-rpc-map.ts","../src/functions/wirings/rpc/pikku-command-rpc.ts","../src/functions/wirings/rpc/serialize-public-rpc.ts","../src/functions/wirings/rpc/serialize-react-query-hooks.ts","../src/functions/wirings/rpc/serialize-remote-rpc.ts","../src/functions/wirings/rpc/serialize-rpc-wrapper.ts","../src/functions/wirings/rpc/serialize-typed-rpc-map.ts","../src/functions/wirings/scheduler/pikku-command-scheduler-types.ts","../src/functions/wirings/scheduler/pikku-command-scheduler.ts","../src/functions/wirings/scheduler/serialize-scheduler-meta.ts","../src/functions/wirings/scheduler/serialize-scheduler-types.ts","../src/functions/wirings/secrets/pikku-command-secrets.ts","../src/functions/wirings/secrets/serialize-secrets-types.ts","../src/functions/wirings/triggers/pikku-command-trigger-types.ts","../src/functions/wirings/triggers/pikku-command-trigger.ts","../src/functions/wirings/triggers/serialize-trigger-meta.ts","../src/functions/wirings/triggers/serialize-trigger-types.ts","../src/functions/wirings/variables/pikku-command-variables.ts","../src/functions/wirings/variables/serialize-variables-types.ts","../src/functions/wirings/workflow/pikku-command-workflow-routes.ts","../src/functions/wirings/workflow/pikku-command-workflow.ts","../src/functions/wirings/workflow/serialize-workflow-bootstrap-map.ts","../src/functions/wirings/workflow/serialize-workflow-map.ts","../src/functions/wirings/workflow/serialize-workflow-meta.ts","../src/functions/wirings/workflow/serialize-workflow-registration.ts","../src/functions/wirings/workflow/serialize-workflow-routes.ts","../src/functions/wirings/workflow/serialize-workflow-types.ts","../src/functions/workflows/all.workflow.ts","../src/middleware/log-command-info-and-time.ts","../src/scaffold/rpc-remote.gen.ts","../src/services/cli-logger-forwarder.service.ts","../src/services/cli-logger.service.ts","../src/utils/check-required-types.ts","../src/utils/command-summary.ts","../src/utils/contract-versions.ts","../src/utils/file-import-path.ts","../src/utils/file-imports-serializer.ts","../src/utils/file-writer.ts","../src/utils/generate-bootstrap-file.ts","../src/utils/get-cli-version.ts","../src/utils/parse-cli-filters.ts","../src/utils/pikku-cli-config.ts","../src/utils/pikku-files-and-methods.ts","../src/utils/serialize-import-map.ts","../src/utils/serialize-meta-ts.ts","../src/utils/serialize-schemas.ts","../src/utils/strip-verbose-meta.ts","../.pikku/pikku-bootstrap.gen.ts","../.pikku/pikku-meta-service.gen.ts","../.pikku/pikku-services.gen.ts","../.pikku/pikku-types.gen.ts","../.pikku/agent/pikku-agent-map.gen.d.ts","../.pikku/agent/pikku-agent-types.gen.ts","../.pikku/channel/pikku-channel-types.gen.ts","../.pikku/cli/pikku-cli-channel.ts","../.pikku/cli/pikku-cli-types.gen.ts","../.pikku/cli/pikku-cli-wirings-meta.gen.ts","../.pikku/cli/pikku-cli-wirings.gen.ts","../.pikku/cli/pikku-cli.gen.ts","../.pikku/console/pikku-node-types.gen.ts","../.pikku/function/pikku-function-types.gen.ts","../.pikku/function/pikku-functions-meta.gen.ts","../.pikku/function/pikku-functions.gen.ts","../.pikku/http/pikku-http-types.gen.ts","../.pikku/http/pikku-http-wirings-map.gen.d.ts","../.pikku/http/pikku-http-wirings-meta.gen.ts","../.pikku/http/pikku-http-wirings.gen.ts","../.pikku/mcp/pikku-mcp-types.gen.ts","../.pikku/node/pikku-node-types.gen.ts","../.pikku/queue/pikku-queue-types.gen.ts","../.pikku/queue/pikku-queue-workers-wirings-map.gen.d.ts","../.pikku/queue/pikku-queue-workers-wirings-meta.gen.ts","../.pikku/queue/pikku-queue-workers-wirings.gen.ts","../.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts","../.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts","../.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.ts","../.pikku/scheduler/pikku-scheduler-types.gen.ts","../.pikku/schemas/register.gen.ts","../.pikku/secrets/pikku-secret-types.gen.ts","../.pikku/secrets/pikku-secrets.gen.ts","../.pikku/trigger/pikku-trigger-types.gen.ts","../.pikku/variables/pikku-variable-types.gen.ts","../.pikku/variables/pikku-variables.gen.ts","../.pikku/workflow/pikku-workflow-map.gen.d.ts","../.pikku/workflow/pikku-workflow-types.gen.ts","../.pikku/workflow/pikku-workflow-wirings-meta.gen.ts","../.pikku/workflow/pikku-workflow-wirings.gen.ts","../types/application-types.d.ts","../types/bun-sqlite.d.ts","../types/config.d.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../bin/pikku.ts","../src/cli.wiring.ts","../src/services.ts","../src/deploy/build-pipeline.ts","../src/deploy/provider-adapter.ts","../src/deploy/server-entry.ts","../src/deploy/analyzer/analyzer.ts","../src/deploy/analyzer/index.ts","../src/deploy/analyzer/manifest.ts","../src/deploy/bundler/bundler.ts","../src/deploy/bundler/dep-extractor.ts","../src/deploy/bundler/index.ts","../src/deploy/bundler/types.ts","../src/deploy/codegen/index.ts","../src/deploy/codegen/per-unit-codegen.ts","../src/deploy/plan/executor.ts","../src/deploy/plan/formatter.ts","../src/deploy/plan/index.ts","../src/deploy/plan/planner.ts","../src/deploy/plan/provider.ts","../src/deploy/plan/types.ts","../src/fabric/fabric-commands.ts","../src/fabric/functions/db-schema.function.ts","../src/fabric/functions/deploy-list.function.ts","../src/fabric/functions/deploy-units.function.ts","../src/fabric/functions/deploy.function.ts","../src/fabric/functions/domains-add.function.ts","../src/fabric/functions/domains-list.function.ts","../src/fabric/functions/domains-remove.function.ts","../src/fabric/functions/errors.function.ts","../src/fabric/functions/init.function.ts","../src/fabric/functions/link.function.ts","../src/fabric/functions/llm-key.function.ts","../src/fabric/functions/login.function.ts","../src/fabric/functions/logs.function.ts","../src/fabric/functions/metrics.function.ts","../src/fabric/functions/rollback.function.ts","../src/fabric/functions/secrets-list.function.ts","../src/fabric/functions/secrets-set.function.ts","../src/fabric/functions/status.function.ts","../src/fabric/functions/trace.function.ts","../src/fabric/functions/validate-core.ts","../src/fabric/functions/validate.function.ts","../src/fabric/lib/config.ts","../src/fabric/lib/console-url.ts","../src/fabric/lib/git.ts","../src/fabric/lib/http.ts","../src/fabric/lib/not-implemented.ts","../src/fabric/lib/output.ts","../src/fabric/lib/prompt.ts","../src/fabric/lib/stage.ts","../src/fabric/sdk/http-map.gen.d.ts","../src/fabric/sdk/pikku-fetch.gen.ts","../src/fabric/sdk/pikku-rpc.gen.ts","../src/fabric/sdk/rpc-map.gen.d.ts","../src/functions/commands/all.ts","../src/functions/commands/binary.ts","../src/functions/commands/bootstrap.ts","../src/functions/commands/console.ts","../src/functions/commands/db-audit.ts","../src/functions/commands/db-generate.ts","../src/functions/commands/db-migrate.ts","../src/functions/commands/db-reset.ts","../src/functions/commands/db-seed.ts","../src/functions/commands/db-shared.ts","../src/functions/commands/deploy-apply.ts","../src/functions/commands/deploy-info.ts","../src/functions/commands/deploy-plan.ts","../src/functions/commands/dev.ts","../src/functions/commands/emails-init.ts","../src/functions/commands/enable.ts","../src/functions/commands/info.ts","../src/functions/commands/load-user-project.ts","../src/functions/commands/login.ts","../src/functions/commands/meta.ts","../src/functions/commands/new-addon.ts","../src/functions/commands/new-function.ts","../src/functions/commands/new-middleware.ts","../src/functions/commands/new-permission.ts","../src/functions/commands/new-wiring.ts","../src/functions/commands/pikku-command-bootstrap.ts","../src/functions/commands/pikku-command-summary.ts","../src/functions/commands/skills.ts","../src/functions/commands/tests-coverage.ts","../src/functions/commands/tests-init.ts","../src/functions/commands/versions-check.ts","../src/functions/commands/versions-init.ts","../src/functions/commands/versions-update.ts","../src/functions/commands/watch.ts","../src/functions/commands/workspace-validate.ts","../src/functions/db/annotation-parser.ts","../src/functions/db/better-auth-schema.ts","../src/functions/db/coercion-plugin.ts","../src/functions/db/db-codegen.ts","../src/functions/db/db-introspector.ts","../src/functions/db/db-migrator.ts","../src/functions/db/local-db.ts","../src/functions/db/zod-codegen.ts","../src/functions/db/postgres/pglite-kysely.ts","../src/functions/db/postgres/postgres-introspector.ts","../src/functions/db/postgres/postgres-migrator.ts","../src/functions/db/sqlite/seed.ts","../src/functions/db/sqlite/sqlite-introspector.ts","../src/functions/db/sqlite/sqlite-kysely.ts","../src/functions/db/sqlite/sqlite-migrator.ts","../src/functions/db/sqlite/sqlite-runtime-bun.ts","../src/functions/db/sqlite/sqlite-runtime-node.ts","../src/functions/db/sqlite/sqlite-runtime.ts","../src/functions/runtimes/fetch/index.ts","../src/functions/runtimes/nextjs/pikku-command-nextjs.ts","../src/functions/runtimes/nextjs/serialize-nextjs-backend-worker-rpc-wrapper.ts","../src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/functions/runtimes/nextjs/serialize-nextjs-http-wrapper.ts","../src/functions/runtimes/tanstack-start/pikku-command-tanstack-start.ts","../src/functions/runtimes/tanstack-start/serialize-tanstack-start-shim.ts","../src/functions/runtimes/websocket/pikku-command-websocket-typed.ts","../src/functions/runtimes/websocket/serialize-websocket-wrapper.ts","../src/functions/validate/workspace-validate.ts","../src/functions/wirings/ai-agent/pikku-command-ai-agent-types.ts","../src/functions/wirings/ai-agent/pikku-command-ai-agent.ts","../src/functions/wirings/ai-agent/pikku-command-public-agent.ts","../src/functions/wirings/ai-agent/serialize-agent-map.ts","../src/functions/wirings/ai-agent/serialize-ai-agent-types.ts","../src/functions/wirings/ai-agent/serialize-public-agent.ts","../src/functions/wirings/auth/pikku-command-auth.ts","../src/functions/wirings/auth/serialize-auth-gen.ts","../src/functions/wirings/auth/serialize-auth-meta.ts","../src/functions/wirings/auth/serialize-auth-types.ts","../src/functions/wirings/channels/pikku-channels.ts","../src/functions/wirings/channels/pikku-command-channel-types.ts","../src/functions/wirings/channels/pikku-command-channels-map.ts","../src/functions/wirings/channels/pikku-command-channels.ts","../src/functions/wirings/channels/serialize-channel-types.ts","../src/functions/wirings/channels/serialize-typed-channel-map.ts","../src/functions/wirings/cli/pikku-command-cli-entry.ts","../src/functions/wirings/cli/pikku-command-cli-types.ts","../src/functions/wirings/cli/pikku-command-cli.ts","../src/functions/wirings/cli/serialize-channel-cli-client.ts","../src/functions/wirings/cli/serialize-channel-cli.ts","../src/functions/wirings/cli/serialize-cli-types.ts","../src/functions/wirings/cli/serialize-local-cli-bootstrap.ts","../src/functions/wirings/console/pikku-command-console-functions.ts","../src/functions/wirings/console/pikku-command-node-types.ts","../src/functions/wirings/console/pikku-command-nodes-meta.ts","../src/functions/wirings/console/serialize-console-functions.ts","../src/functions/wirings/console/serialize-node-types.ts","../src/functions/wirings/credentials/pikku-command-credentials.ts","../src/functions/wirings/credentials/serialize-credentials-types.ts","../src/functions/wirings/emails/pikku-command-emails.ts","../src/functions/wirings/emails/serialize-emails.ts","../src/functions/wirings/functions/pikku-command-addon-types.ts","../src/functions/wirings/functions/pikku-command-function-types-split.ts","../src/functions/wirings/functions/pikku-command-function-types.ts","../src/functions/wirings/functions/pikku-command-functions.ts","../src/functions/wirings/functions/pikku-command-services.ts","../src/functions/wirings/functions/schemas.ts","../src/functions/wirings/functions/serialize-addon-types.ts","../src/functions/wirings/functions/serialize-function-imports.ts","../src/functions/wirings/functions/serialize-function-types.ts","../src/functions/wirings/functions/serialize-pikku-types-hub.ts","../src/functions/wirings/gateway/pikku-command-gateway.ts","../src/functions/wirings/http/pikku-command-http-map.ts","../src/functions/wirings/http/pikku-command-http-routes.ts","../src/functions/wirings/http/pikku-command-http-types.ts","../src/functions/wirings/http/pikku-command-openapi.ts","../src/functions/wirings/http/pikku-http-routes.ts","../src/functions/wirings/http/serialize-fetch-wrapper.ts","../src/functions/wirings/http/serialize-http-types.ts","../src/functions/wirings/http/serialize-typed-http-map.ts","../src/functions/wirings/mcp/pikku-command-mcp-json.ts","../src/functions/wirings/mcp/pikku-command-mcp-types.ts","../src/functions/wirings/mcp/pikku-command-mcp.ts","../src/functions/wirings/mcp/serialize-mcp-types.ts","../src/functions/wirings/middleware/pikku-command-middleware.ts","../src/functions/wirings/middleware/serialize-middleware-imports.ts","../src/functions/wirings/package/pikku-command-package-types.ts","../src/functions/wirings/package/pikku-command-package.ts","../src/functions/wirings/package/serialize-package-types.ts","../src/functions/wirings/package/serialize-package.ts","../src/functions/wirings/permissions/pikku-command-permissions.ts","../src/functions/wirings/permissions/serialize-permissions-imports.ts","../src/functions/wirings/queue/pikku-command-queue-map.ts","../src/functions/wirings/queue/pikku-command-queue-service.ts","../src/functions/wirings/queue/pikku-command-queue-types.ts","../src/functions/wirings/queue/pikku-command-queue.ts","../src/functions/wirings/queue/pikku-queue-map.ts","../src/functions/wirings/queue/pikku-queue.ts","../src/functions/wirings/queue/serialize-queue-map.ts","../src/functions/wirings/queue/serialize-queue-meta.ts","../src/functions/wirings/queue/serialize-queue-types.ts","../src/functions/wirings/queue/serialize-queue-wrapper.ts","../src/functions/wirings/realtime/pikku-command-events-scaffold.ts","../src/functions/wirings/realtime/pikku-command-realtime.ts","../src/functions/wirings/realtime/serialize-events-scaffold.ts","../src/functions/wirings/realtime/serialize-realtime-client.ts","../src/functions/wirings/rpc/pikku-command-public-rpc.ts","../src/functions/wirings/rpc/pikku-command-react-query.ts","../src/functions/wirings/rpc/pikku-command-remote-rpc.ts","../src/functions/wirings/rpc/pikku-command-rpc-client.ts","../src/functions/wirings/rpc/pikku-command-rpc-map.ts","../src/functions/wirings/rpc/pikku-command-rpc.ts","../src/functions/wirings/rpc/serialize-public-rpc.ts","../src/functions/wirings/rpc/serialize-react-query-hooks.ts","../src/functions/wirings/rpc/serialize-remote-rpc.ts","../src/functions/wirings/rpc/serialize-rpc-wrapper.ts","../src/functions/wirings/rpc/serialize-typed-rpc-map.ts","../src/functions/wirings/scheduler/pikku-command-scheduler-types.ts","../src/functions/wirings/scheduler/pikku-command-scheduler.ts","../src/functions/wirings/scheduler/serialize-scheduler-meta.ts","../src/functions/wirings/scheduler/serialize-scheduler-types.ts","../src/functions/wirings/secrets/pikku-command-secrets.ts","../src/functions/wirings/secrets/serialize-secrets-types.ts","../src/functions/wirings/triggers/pikku-command-trigger-types.ts","../src/functions/wirings/triggers/pikku-command-trigger.ts","../src/functions/wirings/triggers/serialize-trigger-meta.ts","../src/functions/wirings/triggers/serialize-trigger-types.ts","../src/functions/wirings/variables/pikku-command-variables.ts","../src/functions/wirings/variables/serialize-variables-types.ts","../src/functions/wirings/workflow/pikku-command-workflow-routes.ts","../src/functions/wirings/workflow/pikku-command-workflow.ts","../src/functions/wirings/workflow/serialize-workflow-bootstrap-map.ts","../src/functions/wirings/workflow/serialize-workflow-map.ts","../src/functions/wirings/workflow/serialize-workflow-meta.ts","../src/functions/wirings/workflow/serialize-workflow-registration.ts","../src/functions/wirings/workflow/serialize-workflow-routes.ts","../src/functions/wirings/workflow/serialize-workflow-types.ts","../src/functions/workflows/all.workflow.ts","../src/middleware/log-command-info-and-time.ts","../src/scaffold/rpc-remote.gen.ts","../src/services/cli-logger-forwarder.service.ts","../src/services/cli-logger.service.ts","../src/utils/check-required-types.ts","../src/utils/cli-session.ts","../src/utils/command-summary.ts","../src/utils/contract-versions.ts","../src/utils/device-auth.ts","../src/utils/file-import-path.ts","../src/utils/file-imports-serializer.ts","../src/utils/file-writer.ts","../src/utils/generate-bootstrap-file.ts","../src/utils/get-cli-version.ts","../src/utils/parse-cli-filters.ts","../src/utils/pikku-cli-config.ts","../src/utils/pikku-files-and-methods.ts","../src/utils/serialize-import-map.ts","../src/utils/serialize-meta-ts.ts","../src/utils/serialize-schemas.ts","../src/utils/strip-verbose-meta.ts","../.pikku/pikku-bootstrap.gen.ts","../.pikku/pikku-meta-service.gen.ts","../.pikku/pikku-services.gen.ts","../.pikku/pikku-types.gen.ts","../.pikku/agent/pikku-agent-map.gen.d.ts","../.pikku/agent/pikku-agent-types.gen.ts","../.pikku/channel/pikku-channel-types.gen.ts","../.pikku/cli/pikku-cli-channel.ts","../.pikku/cli/pikku-cli-types.gen.ts","../.pikku/cli/pikku-cli-wirings-meta.gen.ts","../.pikku/cli/pikku-cli-wirings.gen.ts","../.pikku/cli/pikku-cli.gen.ts","../.pikku/console/pikku-node-types.gen.ts","../.pikku/function/pikku-function-types.gen.ts","../.pikku/function/pikku-functions-meta.gen.ts","../.pikku/function/pikku-functions.gen.ts","../.pikku/http/pikku-http-types.gen.ts","../.pikku/http/pikku-http-wirings-map.gen.d.ts","../.pikku/http/pikku-http-wirings-meta.gen.ts","../.pikku/http/pikku-http-wirings.gen.ts","../.pikku/mcp/pikku-mcp-types.gen.ts","../.pikku/node/pikku-node-types.gen.ts","../.pikku/queue/pikku-queue-types.gen.ts","../.pikku/queue/pikku-queue-workers-wirings-map.gen.d.ts","../.pikku/queue/pikku-queue-workers-wirings-meta.gen.ts","../.pikku/queue/pikku-queue-workers-wirings.gen.ts","../.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts","../.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts","../.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.ts","../.pikku/scheduler/pikku-scheduler-types.gen.ts","../.pikku/schemas/register.gen.ts","../.pikku/secrets/pikku-secret-types.gen.ts","../.pikku/secrets/pikku-secrets.gen.ts","../.pikku/trigger/pikku-trigger-types.gen.ts","../.pikku/variables/pikku-variable-types.gen.ts","../.pikku/variables/pikku-variables.gen.ts","../.pikku/workflow/pikku-workflow-map.gen.d.ts","../.pikku/workflow/pikku-workflow-types.gen.ts","../.pikku/workflow/pikku-workflow-wirings-meta.gen.ts","../.pikku/workflow/pikku-workflow-wirings.gen.ts","../types/application-types.d.ts","../types/bun-sqlite.d.ts","../types/config.d.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.41",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"imports": {
|
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
"prepublishOnly": "yarn build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@electric-sql/pglite": "^0.5.1",
|
|
28
29
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
29
|
-
"@pikku/better-auth": "^0.12.
|
|
30
|
-
"@pikku/core": "^0.12.
|
|
30
|
+
"@pikku/better-auth": "^0.12.7",
|
|
31
|
+
"@pikku/core": "^0.12.34",
|
|
31
32
|
"@pikku/deploy-cloudflare": "^0.12.3",
|
|
32
33
|
"@pikku/fetch": "^0.12.3",
|
|
33
34
|
"@pikku/inspector": "^0.12.20",
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-machine-auth
|
|
3
|
+
description: 'Use when authenticating a CLI/agent/service against a Pikku server, adding machine-to-machine (M2M) auth, issuing scoped API keys for sandboxes/agents/workers, or wiring better-auth sessions into Pikku middleware. Covers `pikku login` (device-authorization), the better-auth API Key plugin, machine identities, and `betterAuthSession` with the api-key branch.
|
|
4
|
+
TRIGGER when: user asks about CLI login, `pikku login`, machine agents, service-to-service auth, API keys, client credentials, sandbox/worker tokens, or resolving a better-auth session in a Pikku function.
|
|
5
|
+
DO NOT TRIGGER when: user asks about end-user HTTP session/cookie auth only (use pikku-http + the app betterAuth config) or about WebSocket channel mechanics (use pikku-websocket).'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Pikku Machine Auth
|
|
9
|
+
|
|
10
|
+
Unified authentication for humans **and** machines against a Pikku + better-auth
|
|
11
|
+
server. Two paths, two headers, one resolver:
|
|
12
|
+
|
|
13
|
+
| Caller | Credential | Header | Obtained by |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| **Human** (CLI, dev) | better-auth session token | `Authorization: Bearer <token>` | `pikku login` (device flow) → `~/.pikku/session.json` |
|
|
16
|
+
| **Machine** (agent, sandbox, worker) | scoped API key | `x-api-key: <key>` | `createApiKey` (server-side, at provision/spawn) |
|
|
17
|
+
|
|
18
|
+
Both resolve to a Pikku `UserSession` through one middleware:
|
|
19
|
+
`betterAuthSession({ mapSession, apiKey: { mapKey } })`.
|
|
20
|
+
|
|
21
|
+
> The literal OAuth `client_credentials` grant is **not** implemented in
|
|
22
|
+
> better-auth's oidc-provider. The API Key plugin gives the same capability (a
|
|
23
|
+
> baked secret a service presents for scoped access), not the wire protocol.
|
|
24
|
+
|
|
25
|
+
## Agent Operating Procedure
|
|
26
|
+
|
|
27
|
+
1. Discover before editing — inspect the app's `betterAuth({ plugins: [...] })`
|
|
28
|
+
config and existing middleware wiring before adding anything.
|
|
29
|
+
2. Server changes go in the auth factory + a middleware wiring file; never put
|
|
30
|
+
auth checks in a function body (use `permissions`).
|
|
31
|
+
3. The API Key plugin contributes an `apikey` table — add the matching SQL
|
|
32
|
+
migration and regenerate DB types before relying on it.
|
|
33
|
+
4. Validate with the narrowest command, then `pikku all`.
|
|
34
|
+
|
|
35
|
+
## Human path — `pikku login`
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pikku login --url https://app.example.com # device-authorization flow
|
|
39
|
+
pikku whoami # show current session + expiry
|
|
40
|
+
pikku logout # remove stored session
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`pikku login` runs the RFC 8628 device flow: it requests a code, opens the
|
|
44
|
+
browser to the verification URL, polls until you approve, then stores the
|
|
45
|
+
session token (keyed by base URL) at `~/.pikku/session.json` with its expiry.
|
|
46
|
+
|
|
47
|
+
**Server requirement** — enable the `deviceAuthorization` and `bearer` plugins:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { deviceAuthorization, bearer } from 'better-auth/plugins'
|
|
51
|
+
|
|
52
|
+
betterAuth({
|
|
53
|
+
// ...
|
|
54
|
+
plugins: [
|
|
55
|
+
deviceAuthorization({ expiresIn: '5min', interval: '5s', schema: {} }),
|
|
56
|
+
bearer(), // lets `Authorization: Bearer <session-token>` resolve a session
|
|
57
|
+
],
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The browser approval is two steps the user's browser does automatically:
|
|
62
|
+
`GET /auth/device?user_code=XXXX` (claims the code while signed in) then
|
|
63
|
+
`POST /auth/device/approve`. The CLI only requests the code and polls
|
|
64
|
+
`POST /auth/device/token`.
|
|
65
|
+
|
|
66
|
+
## Machine path — API keys
|
|
67
|
+
|
|
68
|
+
Install the plugin (separate official package) and enable it:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
yarn add @better-auth/api-key # peer: better-auth ^1.6.19
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { apiKey } from '@better-auth/api-key'
|
|
76
|
+
|
|
77
|
+
betterAuth({
|
|
78
|
+
plugins: [
|
|
79
|
+
apiKey({
|
|
80
|
+
enableMetadata: true, // REQUIRED to store scope on the key
|
|
81
|
+
enableSessionForAPIKeys: true, // lets a key resolve via getSession too
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Identity model
|
|
88
|
+
|
|
89
|
+
A **machine is an API key, not a throwaway user.** Keys are owned by a small set
|
|
90
|
+
of stable **service-user** identities you provision once (e.g. `orchestrator`,
|
|
91
|
+
`machine-agent`, `builder`, `sandbox-runtime`). Per-machine scope rides on the
|
|
92
|
+
key's `metadata`/`permissions`. A key requires a real owning user row — minting
|
|
93
|
+
one for a non-existent `userId` is created but will not resolve.
|
|
94
|
+
|
|
95
|
+
### Mint a scoped key (server-side, at spawn/provision)
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// `auth` is the better-auth instance (injected service)
|
|
99
|
+
const { key } = await auth.api.createApiKey({
|
|
100
|
+
body: {
|
|
101
|
+
userId: sandboxRuntimeUserId, // a stable service user
|
|
102
|
+
name: `sandbox:${sandboxId}`,
|
|
103
|
+
expiresIn: 60 * 60, // seconds
|
|
104
|
+
metadata: { sandboxId }, // keep only STABLE ids here
|
|
105
|
+
permissions: { sandbox: ['read', 'write'] },
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
// inject `key` into the machine's env; it sends it as `x-api-key`.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Rotate by minting a new key and expiring/deleting the old (`deleteApiKey`);
|
|
112
|
+
multiple active keys per identity allow zero-downtime rotation.
|
|
113
|
+
|
|
114
|
+
### Resolve scope — `verifyApiKey`, not `getSession`
|
|
115
|
+
|
|
116
|
+
`getSession(x-api-key)` returns only a bare mock session **without** the
|
|
117
|
+
metadata. Scope must come from `verifyApiKey`, which returns
|
|
118
|
+
`{ valid, key: { userId, metadata, permissions } }`. The
|
|
119
|
+
`betterAuthSession` api-key branch does this for you:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { betterAuthSession } from '@pikku/better-auth'
|
|
123
|
+
import { addHTTPMiddleware } from '@pikku/core/http'
|
|
124
|
+
|
|
125
|
+
addHTTPMiddleware([
|
|
126
|
+
betterAuthSession({
|
|
127
|
+
// human path: getSession result -> app session
|
|
128
|
+
mapSession: ({ user }) => ({ userId: user.id }),
|
|
129
|
+
// machine path: verified key -> app session. `services` lets you resolve
|
|
130
|
+
// CURRENT scope (e.g. look up the owning row) instead of trusting only the
|
|
131
|
+
// baked metadata.
|
|
132
|
+
apiKey: {
|
|
133
|
+
header: 'x-api-key', // default
|
|
134
|
+
mapKey: async (key, services) => {
|
|
135
|
+
const sandboxId = key.metadata?.sandboxId
|
|
136
|
+
if (!sandboxId) return null // reject
|
|
137
|
+
const row = await services.kysely
|
|
138
|
+
.selectFrom('sandboxInstance')
|
|
139
|
+
.innerJoin('sandbox', 'sandbox.id', 'sandboxInstance.sandboxId')
|
|
140
|
+
.select(['sandbox.orgId', 'sandbox.projectId'])
|
|
141
|
+
.where('sandboxInstance.sandboxId', '=', sandboxId)
|
|
142
|
+
.where('sandboxInstance.stoppedAt', 'is', null)
|
|
143
|
+
.executeTakeFirst()
|
|
144
|
+
if (!row) return null
|
|
145
|
+
return { userId: sandboxId, orgId: row.orgId, role: 'sandbox' }
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}),
|
|
149
|
+
])
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When the api-key header is present it is authoritative — the middleware never
|
|
153
|
+
falls through to `getSession` (a bare mock session would shadow the scoped one).
|
|
154
|
+
When it is absent, the human `getSession` path runs as normal.
|
|
155
|
+
|
|
156
|
+
### WebSocket channels authenticate on the upgrade handshake
|
|
157
|
+
|
|
158
|
+
Generated channel CLI clients attach the credential as a connection header
|
|
159
|
+
(`x-api-key` for `PIKKU_API_KEY`, else `Authorization: Bearer` from
|
|
160
|
+
`~/.pikku/session.json`). The `@pikku/ws` server copies the upgrade-request
|
|
161
|
+
headers into the channel's `http.request` and runs the inherited HTTP `*`
|
|
162
|
+
middleware during `runUpgradeMiddleware`, so `betterAuthSession` resolves the
|
|
163
|
+
session before the channel opens. For this to work the app must register
|
|
164
|
+
`betterAuthSession` via `addHTTPMiddleware([...])` (the `*` group) — not only on
|
|
165
|
+
specific routes — so it is inherited into the channel upgrade. Browser clients
|
|
166
|
+
cannot set WebSocket headers, so header-auth only covers the Node CLI path; a
|
|
167
|
+
browser channel needs a query-param/subprotocol vector instead.
|
|
168
|
+
|
|
169
|
+
## Gotchas
|
|
170
|
+
|
|
171
|
+
- `apiKey()` rejects `metadata` unless `enableMetadata: true`.
|
|
172
|
+
- `deviceAuthorization()` requires a `schema` option (pass `schema: {}`).
|
|
173
|
+
- Keep the two paths on **different headers** — `x-api-key` (machine) vs
|
|
174
|
+
`Authorization: Bearer` (human). One header for both reintroduces ambiguity.
|
|
175
|
+
- The `apikey` table is plugin-contributed — add the SQL migration + regen types.
|
|
176
|
+
- `~/.pikku/session.json` is written `0600` and stores the token + expiry; the
|
|
177
|
+
CLI uses the expiry to detect when a re-login is needed.
|