@primusclaw/personal-agent-runtime 0.0.0-stage → 0.1.0
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/dist/LICENSE +37 -0
- package/dist/index.js +5734 -0
- package/dist/sqlite/schema.sql +122 -0
- package/dist/types/claw/answers.d.ts +11 -0
- package/dist/types/claw/assistant.d.ts +27 -0
- package/dist/types/claw/client.d.ts +23 -0
- package/dist/types/claw/http.d.ts +64 -0
- package/dist/types/claw/identity-lookup.d.ts +15 -0
- package/dist/types/claw/index.d.ts +5 -0
- package/dist/types/claw/key-revoker.d.ts +22 -0
- package/dist/types/claw/remote-agents.d.ts +23 -0
- package/dist/types/claw/rule.d.ts +20 -0
- package/dist/types/claw/sessions.d.ts +56 -0
- package/dist/types/claw/settings.d.ts +46 -0
- package/dist/types/claw/sse-stream.d.ts +14 -0
- package/dist/types/claw/work-items.d.ts +22 -0
- package/dist/types/claw/zip.d.ts +15 -0
- package/dist/types/config/env.d.ts +29 -0
- package/dist/types/config/index.d.ts +3 -0
- package/dist/types/config/secrets.d.ts +21 -0
- package/dist/types/config/settings.d.ts +13 -0
- package/dist/types/context/als-request-scope.d.ts +3 -0
- package/dist/types/crypto/fernet.d.ts +37 -0
- package/dist/types/crypto/link-code.d.ts +6 -0
- package/dist/types/crypto/node-crypto.d.ts +2 -0
- package/dist/types/crypto/py-base64.d.ts +8 -0
- package/dist/types/crypto-random.d.ts +3 -0
- package/dist/types/exchange/index.d.ts +2 -0
- package/dist/types/exchange/serial.d.ts +8 -0
- package/dist/types/exchange/token-exchange.d.ts +56 -0
- package/dist/types/github/api.d.ts +44 -0
- package/dist/types/github/app-token.d.ts +46 -0
- package/dist/types/github/credential-client.d.ts +37 -0
- package/dist/types/github/device-flow.d.ts +28 -0
- package/dist/types/github/feedback-config.d.ts +26 -0
- package/dist/types/github/feedback-issues.d.ts +23 -0
- package/dist/types/github/feedback-worker.d.ts +11 -0
- package/dist/types/github/index.d.ts +7 -0
- package/dist/types/github/intent-reader.d.ts +28 -0
- package/dist/types/github/pull-reader.d.ts +22 -0
- package/dist/types/http/claw-events.d.ts +27 -0
- package/dist/types/http/link.d.ts +14 -0
- package/dist/types/http/routes.d.ts +37 -0
- package/dist/types/http/server.d.ts +24 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/line-logger.d.ts +15 -0
- package/dist/types/logging/chat-log.d.ts +26 -0
- package/dist/types/metrics/exposition.d.ts +17 -0
- package/dist/types/metrics/index.d.ts +2 -0
- package/dist/types/metrics/prometheus-metrics.d.ts +46 -0
- package/dist/types/sqlite/approval-repository.d.ts +19 -0
- package/dist/types/sqlite/assistant-repository.d.ts +42 -0
- package/dist/types/sqlite/context.d.ts +38 -0
- package/dist/types/sqlite/database.d.ts +37 -0
- package/dist/types/sqlite/feedback-repository.d.ts +27 -0
- package/dist/types/sqlite/forget.d.ts +6 -0
- package/dist/types/sqlite/github-repository.d.ts +36 -0
- package/dist/types/sqlite/idempotency-repository.d.ts +14 -0
- package/dist/types/sqlite/link-repository.d.ts +27 -0
- package/dist/types/sqlite/member-repository.d.ts +15 -0
- package/dist/types/sqlite/migrations.d.ts +12 -0
- package/dist/types/sqlite/notify-repository.d.ts +17 -0
- package/dist/types/sqlite/observe-repository.d.ts +19 -0
- package/dist/types/sqlite/onboarding-repository.d.ts +20 -0
- package/dist/types/sqlite/open.d.ts +25 -0
- package/dist/types/sqlite/people-repository.d.ts +33 -0
- package/dist/types/sqlite/py-json.d.ts +16 -0
- package/dist/types/sqlite/reencrypt.d.ts +13 -0
- package/dist/types/sqlite/remote-repository.d.ts +24 -0
- package/dist/types/sqlite/remote-state-repository.d.ts +20 -0
- package/dist/types/sqlite/session-repository.d.ts +31 -0
- package/dist/types/sqlite/store.d.ts +67 -0
- package/dist/types/sqlite/turn-repository.d.ts +24 -0
- package/dist/types/system-clock.d.ts +3 -0
- package/package.json +34 -4
- package/README.md +0 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
|
|
2
|
+
CREATE TABLE IF NOT EXISTS links (
|
|
3
|
+
aad_id TEXT PRIMARY KEY, name TEXT NOT NULL, key_enc BLOB NOT NULL, created_at REAL NOT NULL);
|
|
4
|
+
CREATE TABLE IF NOT EXISTS pending_keys (
|
|
5
|
+
code_hash TEXT PRIMARY KEY, key_enc BLOB NOT NULL, expires_at REAL NOT NULL,
|
|
6
|
+
used INTEGER NOT NULL DEFAULT 0);
|
|
7
|
+
CREATE TABLE IF NOT EXISTS link_attempts (
|
|
8
|
+
aad_id TEXT NOT NULL, at REAL NOT NULL);
|
|
9
|
+
DROP TABLE IF EXISTS link_codes;
|
|
10
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
11
|
+
aad_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT '', session_id TEXT NOT NULL, updated_at REAL NOT NULL,
|
|
12
|
+
plugin_id TEXT NOT NULL DEFAULT '', model TEXT NOT NULL DEFAULT '', shared INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (aad_id, scope));
|
|
13
|
+
CREATE TABLE IF NOT EXISTS processed (
|
|
14
|
+
activity_id TEXT PRIMARY KEY, at REAL NOT NULL);
|
|
15
|
+
CREATE TABLE IF NOT EXISTS event_claims (
|
|
16
|
+
event_key TEXT PRIMARY KEY, state TEXT NOT NULL, holder TEXT NOT NULL, until REAL NOT NULL);
|
|
17
|
+
CREATE TABLE IF NOT EXISTS conversation_refs (
|
|
18
|
+
aad_id TEXT PRIMARY KEY, reference_json TEXT NOT NULL, updated_at REAL NOT NULL);
|
|
19
|
+
CREATE TABLE IF NOT EXISTS user_prefs (
|
|
20
|
+
aad_id TEXT PRIMARY KEY, timezone TEXT NOT NULL);
|
|
21
|
+
CREATE TABLE IF NOT EXISTS held_events (
|
|
22
|
+
event_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, event_json TEXT NOT NULL, held_at REAL NOT NULL);
|
|
23
|
+
CREATE TABLE IF NOT EXISTS approvals (
|
|
24
|
+
session_id TEXT NOT NULL, tool_use_id TEXT NOT NULL, aad_id TEXT NOT NULL, tool TEXT NOT NULL,
|
|
25
|
+
expires_at REAL NOT NULL, state TEXT NOT NULL DEFAULT 'pending', created_at REAL NOT NULL,
|
|
26
|
+
card_sent INTEGER NOT NULL DEFAULT 1, card_json TEXT,
|
|
27
|
+
PRIMARY KEY (session_id, tool_use_id));
|
|
28
|
+
CREATE TABLE IF NOT EXISTS notify_subs (
|
|
29
|
+
aad_id TEXT PRIMARY KEY, route TEXT NOT NULL UNIQUE, subscription_id TEXT NOT NULL,
|
|
30
|
+
secret_enc BLOB NOT NULL, created_at REAL NOT NULL);
|
|
31
|
+
CREATE TABLE IF NOT EXISTS notify_sub_attempts (
|
|
32
|
+
aad_id TEXT PRIMARY KEY, at REAL NOT NULL);
|
|
33
|
+
CREATE TABLE IF NOT EXISTS turn_answers (
|
|
34
|
+
session_id TEXT PRIMARY KEY, started_at REAL NOT NULL, delivered_at REAL, boot_id TEXT NOT NULL DEFAULT '',
|
|
35
|
+
prompt TEXT NOT NULL DEFAULT '', ended INTEGER NOT NULL DEFAULT 0, message_id TEXT NOT NULL DEFAULT '',
|
|
36
|
+
turn_id TEXT NOT NULL DEFAULT '');
|
|
37
|
+
CREATE TABLE IF NOT EXISTS answered_messages (
|
|
38
|
+
session_id TEXT NOT NULL, message_id TEXT NOT NULL, delivered_at REAL NOT NULL, PRIMARY KEY (session_id, message_id));
|
|
39
|
+
CREATE TABLE IF NOT EXISTS feedback_reports (
|
|
40
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT, aad_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT '',
|
|
41
|
+
session_id TEXT NOT NULL DEFAULT '', text TEXT NOT NULL, scene TEXT NOT NULL, created_at REAL NOT NULL,
|
|
42
|
+
state TEXT NOT NULL DEFAULT 'pending', issue_number INTEGER, issue_url TEXT NOT NULL DEFAULT '',
|
|
43
|
+
as_comment INTEGER NOT NULL DEFAULT 0, attempts INTEGER NOT NULL DEFAULT 0,
|
|
44
|
+
next_attempt_at REAL NOT NULL DEFAULT 0, last_error TEXT NOT NULL DEFAULT '',
|
|
45
|
+
reporter_name TEXT NOT NULL DEFAULT '', reporter_email TEXT NOT NULL DEFAULT '');
|
|
46
|
+
CREATE INDEX IF NOT EXISTS feedback_reports_by_user ON feedback_reports (aad_id, scope, session_id, created_at);
|
|
47
|
+
CREATE TABLE IF NOT EXISTS feedback_asks (
|
|
48
|
+
aad_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT '', asked_at REAL NOT NULL, PRIMARY KEY (aad_id, scope));
|
|
49
|
+
CREATE TABLE IF NOT EXISTS feedback_offers (
|
|
50
|
+
offer_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, issue_number INTEGER NOT NULL, session_id TEXT NOT NULL,
|
|
51
|
+
turn_id TEXT NOT NULL DEFAULT '', created_at REAL NOT NULL, state TEXT NOT NULL DEFAULT 'open');
|
|
52
|
+
CREATE TABLE IF NOT EXISTS feedback_prompts (
|
|
53
|
+
prompt_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, scope TEXT NOT NULL DEFAULT '', created_at REAL NOT NULL);
|
|
54
|
+
CREATE TABLE IF NOT EXISTS onboarding_cards (
|
|
55
|
+
prompt_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, scope TEXT NOT NULL, teams_id TEXT NOT NULL DEFAULT '',
|
|
56
|
+
name TEXT NOT NULL DEFAULT '', created_at REAL NOT NULL);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS onboarding_cards_by_user ON onboarding_cards (aad_id, scope, created_at);
|
|
58
|
+
CREATE TABLE IF NOT EXISTS onboarding_dms (
|
|
59
|
+
aad_id TEXT PRIMARY KEY, at REAL NOT NULL);
|
|
60
|
+
CREATE TABLE IF NOT EXISTS pending_requests (
|
|
61
|
+
aad_id TEXT PRIMARY KEY, scope TEXT NOT NULL, message_id TEXT NOT NULL, text TEXT NOT NULL,
|
|
62
|
+
reference_json TEXT NOT NULL, created_at REAL NOT NULL);
|
|
63
|
+
CREATE TABLE IF NOT EXISTS github_confirmations (
|
|
64
|
+
confirmation_id TEXT PRIMARY KEY, session_id TEXT NOT NULL, actor_aad TEXT NOT NULL, requester_aad TEXT NOT NULL,
|
|
65
|
+
repo TEXT NOT NULL, pr INTEGER NOT NULL, reference_json TEXT NOT NULL, expires_at REAL NOT NULL,
|
|
66
|
+
state TEXT NOT NULL DEFAULT 'pending', created_at REAL NOT NULL,
|
|
67
|
+
kind TEXT NOT NULL DEFAULT 'merge', event TEXT NOT NULL DEFAULT '');
|
|
68
|
+
CREATE TABLE IF NOT EXISTS acting_sessions (
|
|
69
|
+
session_id TEXT PRIMARY KEY, requester_aad TEXT NOT NULL, actor_aad TEXT NOT NULL, created_at REAL NOT NULL);
|
|
70
|
+
CREATE TABLE IF NOT EXISTS review_notices (session_id TEXT, actor_aad TEXT, requester_aad TEXT, pull TEXT, delivered INTEGER,
|
|
71
|
+
reason TEXT, at REAL);
|
|
72
|
+
CREATE TABLE IF NOT EXISTS assistant_messages (
|
|
73
|
+
channel_message_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, actions TEXT NOT NULL, sent_at REAL NOT NULL);
|
|
74
|
+
CREATE TABLE IF NOT EXISTS teams_users (
|
|
75
|
+
teams_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, name TEXT NOT NULL DEFAULT '', updated_at REAL NOT NULL);
|
|
76
|
+
CREATE TABLE IF NOT EXISTS github_notices (
|
|
77
|
+
aad_id TEXT NOT NULL, kind TEXT NOT NULL, at REAL NOT NULL, PRIMARY KEY (aad_id, kind));
|
|
78
|
+
CREATE TABLE IF NOT EXISTS recent_repos (
|
|
79
|
+
aad_id TEXT NOT NULL, repo_key TEXT NOT NULL, repo TEXT NOT NULL, branch TEXT NOT NULL DEFAULT '', seq INTEGER NOT NULL,
|
|
80
|
+
PRIMARY KEY (aad_id, repo_key, branch));
|
|
81
|
+
CREATE TABLE IF NOT EXISTS pending_repo (
|
|
82
|
+
aad_id TEXT PRIMARY KEY, repo TEXT NOT NULL, branch TEXT NOT NULL DEFAULT '', set_at REAL NOT NULL);
|
|
83
|
+
CREATE TABLE IF NOT EXISTS session_history (
|
|
84
|
+
aad_id TEXT NOT NULL, session_id TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', repo TEXT NOT NULL DEFAULT '',
|
|
85
|
+
branch TEXT NOT NULL DEFAULT '', last_at REAL NOT NULL, seq INTEGER NOT NULL, PRIMARY KEY (aad_id, session_id));
|
|
86
|
+
CREATE TABLE IF NOT EXISTS remote_state (
|
|
87
|
+
aad_id TEXT PRIMARY KEY, session_id TEXT, connector_id TEXT NOT NULL DEFAULT '', agent_id TEXT NOT NULL DEFAULT '',
|
|
88
|
+
agent_label TEXT NOT NULL DEFAULT '', host TEXT NOT NULL DEFAULT '', cwd TEXT NOT NULL DEFAULT '',
|
|
89
|
+
cursor TEXT NOT NULL DEFAULT '', turn_open INTEGER NOT NULL DEFAULT 0, mode TEXT NOT NULL DEFAULT '',
|
|
90
|
+
modes_json TEXT NOT NULL DEFAULT '[]', listing_json TEXT NOT NULL DEFAULT '[]', updated_at REAL NOT NULL,
|
|
91
|
+
unattended INTEGER, mode_flags_json TEXT NOT NULL DEFAULT '{}');
|
|
92
|
+
CREATE TABLE IF NOT EXISTS remote_session_names (
|
|
93
|
+
session_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', conversation_ref TEXT NOT NULL DEFAULT '',
|
|
94
|
+
created_at REAL NOT NULL, acp_session_id TEXT NOT NULL DEFAULT '');
|
|
95
|
+
CREATE TABLE IF NOT EXISTS remote_permissions (
|
|
96
|
+
session_id TEXT NOT NULL, action_id TEXT NOT NULL, aad_id TEXT NOT NULL, tool TEXT NOT NULL,
|
|
97
|
+
options_json TEXT NOT NULL, expires_at REAL NOT NULL, state TEXT NOT NULL DEFAULT 'pending', created_at REAL NOT NULL,
|
|
98
|
+
PRIMARY KEY (session_id, action_id));
|
|
99
|
+
CREATE TABLE IF NOT EXISTS personal_clicks (
|
|
100
|
+
aad_id TEXT PRIMARY KEY, at REAL NOT NULL);
|
|
101
|
+
CREATE TABLE IF NOT EXISTS assistant_cards (
|
|
102
|
+
card_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, item_id TEXT NOT NULL, version INTEGER NOT NULL,
|
|
103
|
+
can_accept INTEGER NOT NULL, state TEXT NOT NULL DEFAULT 'open', created_at REAL NOT NULL);
|
|
104
|
+
CREATE TABLE IF NOT EXISTS auto_replies (
|
|
105
|
+
aad_id TEXT NOT NULL, item_id TEXT NOT NULL, pull TEXT NOT NULL, created_at REAL NOT NULL, PRIMARY KEY (aad_id, item_id));
|
|
106
|
+
CREATE TABLE IF NOT EXISTS assistant_action_cards (
|
|
107
|
+
card_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, channel_message_id TEXT NOT NULL, name TEXT NOT NULL, args TEXT NOT NULL,
|
|
108
|
+
state TEXT NOT NULL DEFAULT 'open', created_at REAL NOT NULL);
|
|
109
|
+
CREATE TABLE IF NOT EXISTS assistant_confirm_cards (
|
|
110
|
+
card_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, confirmation_id TEXT NOT NULL, kind TEXT NOT NULL, label TEXT NOT NULL DEFAULT '',
|
|
111
|
+
state TEXT NOT NULL DEFAULT 'open', created_at REAL NOT NULL);
|
|
112
|
+
CREATE TABLE IF NOT EXISTS asker_follows (
|
|
113
|
+
item_id TEXT PRIMARY KEY, batch_id TEXT NOT NULL, review_json TEXT NOT NULL, reference_json TEXT NOT NULL,
|
|
114
|
+
said TEXT NOT NULL DEFAULT '', reviewer_told INTEGER NOT NULL DEFAULT 0, deadline REAL NOT NULL, created_at REAL NOT NULL);
|
|
115
|
+
CREATE TABLE IF NOT EXISTS observed_groups (
|
|
116
|
+
conversation_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, scope TEXT NOT NULL, reference_json TEXT NOT NULL,
|
|
117
|
+
name TEXT NOT NULL DEFAULT '', on_since REAL, updated_at REAL NOT NULL);
|
|
118
|
+
CREATE TABLE IF NOT EXISTS observe_cards (
|
|
119
|
+
card_id TEXT PRIMARY KEY, aad_id TEXT NOT NULL, conversation_id TEXT NOT NULL, run_id TEXT NOT NULL, seq INTEGER NOT NULL,
|
|
120
|
+
tool TEXT NOT NULL, args TEXT NOT NULL, message TEXT NOT NULL DEFAULT '', state TEXT NOT NULL DEFAULT 'open',
|
|
121
|
+
created_at REAL NOT NULL);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS observe_cards_by_suggestion ON observe_cards (aad_id, run_id, seq);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type JsonObject } from "@primusclaw/personal-agent";
|
|
2
|
+
/** The answer's `data` object. */
|
|
3
|
+
export declare function dataOf(answer: unknown, what: string): JsonObject;
|
|
4
|
+
/** The answer's `data` list, the objects in it only. */
|
|
5
|
+
export declare function rowsOf(answer: unknown, what: string): JsonObject[];
|
|
6
|
+
/** The answer's `data`, whatever it is (an answer without the field is not one). */
|
|
7
|
+
export declare function anyDataOf(answer: unknown, what: string): unknown;
|
|
8
|
+
/** The answer itself, as an object. */
|
|
9
|
+
export declare function objectOf(answer: unknown, what: string): Record<string, unknown>;
|
|
10
|
+
/** `message_id` of a message Claw took, or null when it queued it behind a running turn (`{"queued": true}`). */
|
|
11
|
+
export declare function messageIdOf(answer: unknown): string | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type AssistantMessageOptions, type ClawAssistantPort, type ClawCredential, type JsonObject, type ReviewStart, type StartReviews } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** A message to the assistant only has to be taken: its answer comes back as an event. */
|
|
4
|
+
export declare const ASSISTANT_SEND_TIMEOUT_MS = 15000;
|
|
5
|
+
/** What Claw takes of the asker's own words, which it quotes to the reviewer as untrusted. */
|
|
6
|
+
export declare const REQUESTER_NOTE_MAX = 2000;
|
|
7
|
+
/** A review asked of someone else carries that person's exchanged token and own linked key in these. */
|
|
8
|
+
export declare const ACTING_TOKEN_HEADER = "x-acting-as-token";
|
|
9
|
+
export declare const ACTING_KEY_HEADER = "x-acting-as-api-key";
|
|
10
|
+
/** `ClawAssistantPort` over Claw's HTTP API. */
|
|
11
|
+
export declare class HttpAssistant implements ClawAssistantPort {
|
|
12
|
+
private readonly http;
|
|
13
|
+
constructor(http: ClawHttp);
|
|
14
|
+
session(key: ClawCredential): Promise<{
|
|
15
|
+
sessionId: string;
|
|
16
|
+
created: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
send(key: ClawCredential, sessionId: string, text: string, options?: AssistantMessageOptions): Promise<string | null>;
|
|
19
|
+
brief(key: ClawCredential, lang: string, timezone: string): Promise<JsonObject>;
|
|
20
|
+
decideConfirmation(token: ClawCredential, confirmationId: string, decision: string): Promise<JsonObject>;
|
|
21
|
+
startReviews(token: ClawCredential, request: StartReviews): Promise<ReviewStart[]>;
|
|
22
|
+
observeScope(key: ClawCredential, scope: string): Promise<JsonObject>;
|
|
23
|
+
observe(key: ClawCredential, scope: string, content: string, messageId: string): Promise<JsonObject>;
|
|
24
|
+
observations(key: ClawCredential, scope: string, limit?: number): Promise<JsonObject[]>;
|
|
25
|
+
}
|
|
26
|
+
/** `data` when it is an object, else the answer itself (Python's `out["data"] if isinstance(...) else out`). */
|
|
27
|
+
export declare function dataOrSelf(out: Record<string, unknown>): JsonObject;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ClawPort, type Clock, type Logger } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type Fetch } from "./http.js";
|
|
3
|
+
/** What Claw's audit trail calls this client by default (`X-Claw-Client`). */
|
|
4
|
+
export declare const DEFAULT_CLIENT_LABEL = "clawy";
|
|
5
|
+
/** How the Claw client is made (from the worker's settings). */
|
|
6
|
+
export interface ClawClientOptions {
|
|
7
|
+
/** CLAW_API_URL, without a trailing slash. */
|
|
8
|
+
readonly baseUrl: string;
|
|
9
|
+
readonly clock: Clock;
|
|
10
|
+
readonly logger?: Logger;
|
|
11
|
+
/** CLAW_PLUGIN_ID (null: plain sessions). */
|
|
12
|
+
readonly pluginId?: number | null;
|
|
13
|
+
/** CLAWY_MODEL ("" sends none). */
|
|
14
|
+
readonly model?: string;
|
|
15
|
+
/** CLAW_RULE_TOOL_ID (null: no rule rides on a message). */
|
|
16
|
+
readonly ruleToolId?: number | null;
|
|
17
|
+
/** What Claw's audit trail calls this client. */
|
|
18
|
+
readonly clientLabel?: string;
|
|
19
|
+
/** The `fetch` to use (a test passes a fake). */
|
|
20
|
+
readonly fetch?: Fetch;
|
|
21
|
+
}
|
|
22
|
+
/** Claw's public HTTP API, always called as one person with that person's own credential. */
|
|
23
|
+
export declare function createClawClient(options: ClawClientOptions): ClawPort;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ClawUnreachable, type ClawCredential } from "@primusclaw/personal-agent";
|
|
2
|
+
/** What every call waits for at most, unless it says otherwise (the Python worker's client timeout). */
|
|
3
|
+
export declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
4
|
+
/** The `fetch` this client uses; a test passes a fake. */
|
|
5
|
+
export type Fetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
6
|
+
/** One request to Claw. */
|
|
7
|
+
export interface ClawRequest {
|
|
8
|
+
readonly method: string;
|
|
9
|
+
/** Path and query, starting with `/`; every id in it already passed `segment` or `pyQuote`. */
|
|
10
|
+
readonly path: string;
|
|
11
|
+
readonly key: ClawCredential;
|
|
12
|
+
readonly body?: unknown;
|
|
13
|
+
/** A multipart body instead of JSON (`fetch` writes its content type). */
|
|
14
|
+
readonly form?: FormData;
|
|
15
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
16
|
+
readonly timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `value` for one segment of a Claw request path. Ids reach this client from stored rows and from card actions a person
|
|
20
|
+
* can edit, so anything that could change which endpoint or host a request reaches is refused before a request is made,
|
|
21
|
+
* as ClawError(404): the answer Claw gives for an id that is not the caller's.
|
|
22
|
+
*/
|
|
23
|
+
export declare function segment(value: string | number): string;
|
|
24
|
+
/** Python's `urllib.parse.quote(value, safe="")`: everything but letters, digits and `_.-~` percent-encoded (UTF-8). */
|
|
25
|
+
export declare function pyQuote(value: string): string;
|
|
26
|
+
/** The bearer header of a credential. */
|
|
27
|
+
export declare function auth(key: ClawCredential): Record<string, string>;
|
|
28
|
+
/** Refuses a call that must carry an exchanged identity token when `key` is an API key, before anything goes out. */
|
|
29
|
+
export declare function requireToken(key: ClawCredential, what: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* The header that carries a person's own linked key next to an identity token, or none. It never rides next to an `ak-`
|
|
32
|
+
* credential, which would make it a second key of the same person on a request Claw refuses for a key.
|
|
33
|
+
*/
|
|
34
|
+
export declare function modelKeyHeader(key: ClawCredential, modelKey: string | undefined): Record<string, string>;
|
|
35
|
+
/** The low-level HTTP of the Claw client: one place that sends, times out, and turns failures into core's errors. */
|
|
36
|
+
export declare class ClawHttp {
|
|
37
|
+
readonly baseUrl: string;
|
|
38
|
+
private readonly fetch;
|
|
39
|
+
constructor(baseUrl: string, fetchImpl: Fetch);
|
|
40
|
+
/** Sends the request; the response whatever its status. Network failures and timeouts are ClawUnreachable. */
|
|
41
|
+
send(request: ClawRequest, extra?: RequestInit): Promise<Response>;
|
|
42
|
+
/** A call whose answer is JSON; an error status is ClawError, a body that is not JSON ClawAnswerInvalid. */
|
|
43
|
+
json(request: ClawRequest): Promise<unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* A GET whose reply is refused (ReplayRefused) once it is larger than `maxBytes`, before it is decoded: it is read in
|
|
46
|
+
* pieces and never held whole beyond that. Asked for and read as it is on the wire (`Accept-Encoding: identity`); a
|
|
47
|
+
* reply that comes content-encoded all the same is refused unread. An error reply is only quoted (512 bytes).
|
|
48
|
+
*/
|
|
49
|
+
boundedJson(request: ClawRequest, maxBytes: number, signal?: AbortSignal): Promise<unknown>;
|
|
50
|
+
/**
|
|
51
|
+
* A GET's status and body, the body refused (ReplayRefused) once it is larger than `maxBytes`, read in pieces and
|
|
52
|
+
* never held whole beyond that; an error body is cut at 512 bytes. Asked for and read as it is on the wire.
|
|
53
|
+
*/
|
|
54
|
+
boundedBytes(request: ClawRequest, maxBytes: number, signal?: AbortSignal): Promise<{
|
|
55
|
+
status: number;
|
|
56
|
+
bytes: Uint8Array;
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
/** The first 200 characters of an error answer. */
|
|
60
|
+
export declare function head(text: string): string;
|
|
61
|
+
/** The body as text; a connection that breaks while it is read is ClawUnreachable. */
|
|
62
|
+
export declare function readText(response: Response, what: string): Promise<string>;
|
|
63
|
+
/** A failure to reach Claw, as core's error: never connected, timed out (or aborted), or broken after it was sent. */
|
|
64
|
+
export declare function unreachable(err: unknown, what: string): ClawUnreachable;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type IdentityLookup, type KeyOwner } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type Fetch } from "./http.js";
|
|
3
|
+
/**
|
|
4
|
+
* Who a key belongs to, from the platform's `/users/self` asked with the key itself (the Python worker's `safe.whoami`,
|
|
5
|
+
* `safe.safe_user` and `safe.user_id_of`, which read the same answer). Any failure is no owner: an answer other than
|
|
6
|
+
* 200, a timeout or a broken connection, a body that is not JSON or not an object. The caller says so, or tries again
|
|
7
|
+
* later (the subscriptions' timer that fills in Claw user ids).
|
|
8
|
+
*/
|
|
9
|
+
export declare class HttpIdentityLookup implements IdentityLookup {
|
|
10
|
+
private readonly timeoutMs;
|
|
11
|
+
private readonly http;
|
|
12
|
+
/** `safeApiUrl` is the platform API without a trailing slash (Settings.safeApiUrl). */
|
|
13
|
+
constructor(safeApiUrl: string, fetchImpl?: Fetch, timeoutMs?: number);
|
|
14
|
+
ownerOfKey(key: string): Promise<KeyOwner | undefined>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createClawClient, DEFAULT_CLIENT_LABEL, type ClawClientOptions } from "./client.js";
|
|
2
|
+
export type { Fetch } from "./http.js";
|
|
3
|
+
export { HttpIdentityLookup } from "./identity-lookup.js";
|
|
4
|
+
export { HttpKeyRevoker } from "./key-revoker.js";
|
|
5
|
+
export { ruleText } from "./rule.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type KeyRevoker } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type Fetch } from "./http.js";
|
|
3
|
+
/**
|
|
4
|
+
* Deletes a person's own platform key with the key itself (the Python worker's `safe.revoke_own_key`): it asks
|
|
5
|
+
* `/apikeys/current` for the key's id, then deletes `/apikeys/{id}`. True only when the delete answered below 300. Any
|
|
6
|
+
* failure is false, and nothing is thrown for it: an answer other than 200 to the first call, a timeout or a broken
|
|
7
|
+
* connection, a body that is not JSON, not an object or without an id. The caller tells the person to delete the key
|
|
8
|
+
* themselves; the link goes either way.
|
|
9
|
+
*
|
|
10
|
+
* The id goes into the delete's path only as one path segment (`pathId`): a positive integer, or a string of URL
|
|
11
|
+
* unreserved characters that is not a dot segment. For any other id nothing is deleted and the answer is false: the
|
|
12
|
+
* HTTP client would resolve "../users/self" into a request for another endpoint.
|
|
13
|
+
*/
|
|
14
|
+
export declare class HttpKeyRevoker implements KeyRevoker {
|
|
15
|
+
private readonly timeoutMs;
|
|
16
|
+
private readonly http;
|
|
17
|
+
/** `safeApiUrl` is the platform API without a trailing slash (Settings.safeApiUrl). */
|
|
18
|
+
constructor(safeApiUrl: string, fetchImpl?: Fetch, timeoutMs?: number);
|
|
19
|
+
revoke(key: string): Promise<boolean>;
|
|
20
|
+
/** The key's id from `/apikeys/current` (Python: `body.get("id") or (body.get("data") or {}).get("id")`), or none. */
|
|
21
|
+
private currentKeyId;
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ClawCredential, type ClawRemoteAgentsPort, type JsonObject, type NewRemoteSession, type RemoteConnector, type RemoteEventRow, type RemoteHidden, type RemotePairing, type RemoteSession, type RemoteSetup, type SseEvent } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** `ClawRemoteAgentsPort` over Claw's HTTP API; every call names this client (`X-Claw-Client`) in Claw's audit trail. */
|
|
4
|
+
export declare class HttpRemoteAgents implements ClawRemoteAgentsPort {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly http;
|
|
7
|
+
private readonly clientLabel;
|
|
8
|
+
constructor(http: ClawHttp, clientLabel: string);
|
|
9
|
+
setup(key: ClawCredential): Promise<RemoteSetup>;
|
|
10
|
+
connectors(key: ClawCredential, options?: {
|
|
11
|
+
includeHidden?: boolean;
|
|
12
|
+
}): Promise<RemoteConnector[]>;
|
|
13
|
+
pair(key: ClawCredential): Promise<RemotePairing>;
|
|
14
|
+
hide(key: ClawCredential, connectorId: string, refs: readonly string[], hidden: boolean): Promise<RemoteHidden>;
|
|
15
|
+
revoke(key: ClawCredential, connectorId: string): Promise<void>;
|
|
16
|
+
createSession(key: ClawCredential, session: NewRemoteSession): Promise<RemoteSession>;
|
|
17
|
+
sessions(key: ClawCredential): Promise<RemoteSession[]>;
|
|
18
|
+
session(key: ClawCredential, sessionId: string): Promise<RemoteSession>;
|
|
19
|
+
command(key: ClawCredential, sessionId: string, body: Readonly<JsonObject>, idempotencyKey?: string): Promise<JsonObject>;
|
|
20
|
+
decide(key: ClawCredential, sessionId: string, actionId: string, optionId: string): Promise<JsonObject>;
|
|
21
|
+
events(key: ClawCredential, sessionId: string, after?: string, limit?: number): Promise<RemoteEventRow[]>;
|
|
22
|
+
stream(key: ClawCredential, sessionId: string, after?: string, signal?: AbortSignal): AsyncIterable<SseEvent>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ClawCredential, type Clock, type Logger } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** A rule download is a zip of one file of at most RULE_MAX_BYTES; a larger answer is refused before it is held. */
|
|
4
|
+
export declare const RULE_DOWNLOAD_MAX_BYTES: number;
|
|
5
|
+
/** The text of a rule tool as `GET /v1/tools/<id>/download` returns it: a zip holding one small markdown file. */
|
|
6
|
+
export declare function ruleText(download: Uint8Array): string;
|
|
7
|
+
/**
|
|
8
|
+
* The reply-style rule: a public rule tool of Claw (CLAW_RULE_TOOL_ID), read with the caller's key and kept for
|
|
9
|
+
* RULE_TTL_MS. When it cannot be read the last copy is used, else none: the person's message still goes.
|
|
10
|
+
*/
|
|
11
|
+
export declare class StyleRule {
|
|
12
|
+
private readonly http;
|
|
13
|
+
private readonly toolId;
|
|
14
|
+
private readonly clock;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private copy;
|
|
17
|
+
constructor(http: ClawHttp, toolId: number, clock: Clock, logger: Logger);
|
|
18
|
+
text(key: ClawCredential): Promise<string>;
|
|
19
|
+
private download;
|
|
20
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ClawCredential, type ClawSessionsPort, type JsonObject, type NewSession, type SendOptions, type SseEvent, type UploadFile } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
import type { StyleRule } from "./rule.js";
|
|
4
|
+
/** Claw takes a file for this many days (one of its tiers: 1, 2, 7, 15, 30). */
|
|
5
|
+
export declare const UPLOAD_TTL_DAYS = 7;
|
|
6
|
+
/** The newest event alone is a few KB; more is a Claw that ignored `tail=1` and sent everything. */
|
|
7
|
+
export declare const LAST_EVENT_MAX_BYTES = 262144;
|
|
8
|
+
/** A changes reply lists at most 200 files per repository: a few hundred KB at the very most. */
|
|
9
|
+
export declare const CHANGES_MAX_BYTES = 1048576;
|
|
10
|
+
/** What a session's messages carry besides the text. */
|
|
11
|
+
export interface MessageDefaults {
|
|
12
|
+
/** CLAW_PLUGIN_ID, named as the web client names it (`pluginId`); null none. */
|
|
13
|
+
readonly pluginId: number | null;
|
|
14
|
+
/** CLAWY_MODEL: what a new session asks for when its caller names none ("" none). */
|
|
15
|
+
readonly model: string;
|
|
16
|
+
/** The reply-style rule, when a rule tool is configured. */
|
|
17
|
+
readonly rule: StyleRule | null;
|
|
18
|
+
}
|
|
19
|
+
/** `ClawSessionsPort` over Claw's HTTP API. */
|
|
20
|
+
export declare class HttpSessions implements ClawSessionsPort {
|
|
21
|
+
#private;
|
|
22
|
+
private readonly http;
|
|
23
|
+
private readonly defaults;
|
|
24
|
+
constructor(http: ClawHttp, defaults: MessageDefaults);
|
|
25
|
+
checkKey(key: ClawCredential): Promise<void>;
|
|
26
|
+
create(key: ClawCredential, session: NewSession): Promise<{
|
|
27
|
+
sessionId: string;
|
|
28
|
+
messageId: string;
|
|
29
|
+
}>;
|
|
30
|
+
uploadFiles(key: ClawCredential, sessionId: string, folder: string, files: readonly UploadFile[]): Promise<{
|
|
31
|
+
paths: string[];
|
|
32
|
+
refused: string[];
|
|
33
|
+
}>;
|
|
34
|
+
setModel(key: ClawCredential, sessionId: string, model: string): Promise<void>;
|
|
35
|
+
sendMessage(key: ClawCredential, sessionId: string, text: string, options?: SendOptions): Promise<string | null>;
|
|
36
|
+
turn(key: ClawCredential, sessionId: string): Promise<{
|
|
37
|
+
running: boolean;
|
|
38
|
+
messageId: string | null;
|
|
39
|
+
}>;
|
|
40
|
+
events(key: ClawCredential, sessionId: string, options?: {
|
|
41
|
+
tail?: number;
|
|
42
|
+
maxBytes?: number;
|
|
43
|
+
}): Promise<JsonObject[]>;
|
|
44
|
+
lastEventId(key: ClawCredential, sessionId: string): Promise<string | null>;
|
|
45
|
+
changes(key: ClawCredential, sessionId: string, timeoutMs: number): Promise<unknown>;
|
|
46
|
+
deliveryLink(sessionId: string, deliveryId: string): string | null;
|
|
47
|
+
interrupt(key: ClawCredential, sessionId: string): Promise<void>;
|
|
48
|
+
decide(key: ClawCredential, sessionId: string, toolUseId: string, action: string, options?: {
|
|
49
|
+
acting?: boolean;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
answer(key: ClawCredential, sessionId: string, toolUseId: string, answers: Readonly<Record<string, string>>, skipped: readonly string[], options?: {
|
|
52
|
+
acting?: boolean;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
vote(key: ClawCredential, sessionId: string, messageId: string, vote: "up" | "down", reason?: string): Promise<void>;
|
|
55
|
+
stream(key: ClawCredential, sessionId: string, after?: string, signal?: AbortSignal, onOpen?: () => void): AsyncIterable<SseEvent>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type ClawCredential, type ClawMergeConfirmationsPort, type ClawNotificationsPort, type ClawSettingsPort, type ClawSubscription, type ClawTriggersPort, type GithubConnection, type JsonObject, type MemoryState, type TriggerInput } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** `ClawSettingsPort` over Claw's HTTP API. */
|
|
4
|
+
export declare class HttpSettings implements ClawSettingsPort {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly http;
|
|
7
|
+
constructor(http: ClawHttp);
|
|
8
|
+
memory(key: ClawCredential, limit?: number): Promise<MemoryState>;
|
|
9
|
+
setMemory(key: ClawCredential, enabled: boolean): Promise<{
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
available: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
models(key: ClawCredential): Promise<string[]>;
|
|
14
|
+
assistantSettings(token: ClawCredential): Promise<JsonObject | null>;
|
|
15
|
+
reviewMode(token: ClawCredential): Promise<string>;
|
|
16
|
+
setReviewMode(token: ClawCredential, mode: string): Promise<void>;
|
|
17
|
+
githubConnection(token: ClawCredential): Promise<GithubConnection | null>;
|
|
18
|
+
}
|
|
19
|
+
/** `ClawNotificationsPort` over Claw's HTTP API. */
|
|
20
|
+
export declare class HttpNotifications implements ClawNotificationsPort {
|
|
21
|
+
private readonly http;
|
|
22
|
+
constructor(http: ClawHttp);
|
|
23
|
+
list(key: ClawCredential): Promise<ClawSubscription[]>;
|
|
24
|
+
create(key: ClawCredential, url: string, eventTypes?: readonly string[], sources?: readonly string[]): Promise<ClawSubscription>;
|
|
25
|
+
updateTypes(key: ClawCredential, subscriptionId: string, eventTypes: readonly string[], options?: {
|
|
26
|
+
enable?: boolean;
|
|
27
|
+
sources?: readonly string[];
|
|
28
|
+
}): Promise<ClawSubscription>;
|
|
29
|
+
delete(key: ClawCredential, subscriptionId: string): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** `ClawTriggersPort` over Claw's HTTP API. */
|
|
32
|
+
export declare class HttpTriggers implements ClawTriggersPort {
|
|
33
|
+
private readonly http;
|
|
34
|
+
constructor(http: ClawHttp);
|
|
35
|
+
list(key: ClawCredential): Promise<JsonObject[]>;
|
|
36
|
+
create(key: ClawCredential, trigger: TriggerInput): Promise<JsonObject>;
|
|
37
|
+
update(key: ClawCredential, triggerId: string, trigger: TriggerInput): Promise<JsonObject>;
|
|
38
|
+
delete(key: ClawCredential, triggerId: string): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
/** `ClawMergeConfirmationsPort` over Claw's HTTP API. */
|
|
41
|
+
export declare class HttpMergeConfirmations implements ClawMergeConfirmationsPort {
|
|
42
|
+
private readonly http;
|
|
43
|
+
constructor(http: ClawHttp);
|
|
44
|
+
pending(key: ClawCredential, sessionId: string): Promise<JsonObject[]>;
|
|
45
|
+
decide(token: ClawCredential, confirmationId: string, decision: string): Promise<JsonObject>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ClawCredential, type SseEvent } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** Claw sends a keepalive every 15 s; silence this long means a dead stream. */
|
|
4
|
+
export declare const STREAM_IDLE_MS = 90000;
|
|
5
|
+
/** Options of an SSE read (tests shorten the waits). */
|
|
6
|
+
export interface StreamTiming {
|
|
7
|
+
readonly connectMs?: number;
|
|
8
|
+
readonly idleMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The events of a Claw SSE stream at `path` (history after `after`, then live). The connection is given `connectMs` to
|
|
12
|
+
* answer and is dropped (ClawUnreachable "timeout") after `idleMs` with nothing read; `signal` ends it early.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sseEvents(http: ClawHttp, path: string, key: ClawCredential, after: string | undefined, signal?: AbortSignal, timing?: StreamTiming, onOpen?: () => void): AsyncGenerator<SseEvent>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type AskerView, type BoardState, type ClawCredential, type ClawWorkItemsPort, type Decision, type JsonObject, type ReviewAskedOfOther, type WorkItem } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type ClawHttp } from "./http.js";
|
|
3
|
+
/** `ClawWorkItemsPort` over Claw's HTTP API. */
|
|
4
|
+
export declare class HttpWorkItems implements ClawWorkItemsPort {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly http;
|
|
7
|
+
constructor(http: ClawHttp);
|
|
8
|
+
list(key: ClawCredential, state?: BoardState): Promise<WorkItem[]>;
|
|
9
|
+
askedOfOthers(key: ClawCredential): Promise<ReviewAskedOfOther[]>;
|
|
10
|
+
get(key: ClawCredential, itemId: string): Promise<WorkItem>;
|
|
11
|
+
requesterView(key: ClawCredential, itemId: string): Promise<AskerView>;
|
|
12
|
+
accept(key: ClawCredential, itemId: string, version: number): Promise<Decision>;
|
|
13
|
+
retry(key: ClawCredential, itemId: string): Promise<Decision>;
|
|
14
|
+
stop(key: ClawCredential, itemId: string): Promise<Decision>;
|
|
15
|
+
pulls(key: ClawCredential, itemId: string): Promise<{
|
|
16
|
+
item: WorkItem;
|
|
17
|
+
pulls: string[];
|
|
18
|
+
}>;
|
|
19
|
+
watchPull(token: ClawCredential, itemId: string, pull: string, reply?: boolean): Promise<JsonObject>;
|
|
20
|
+
watches(key: ClawCredential, itemId: string): Promise<JsonObject[]>;
|
|
21
|
+
unwatchPull(key: ClawCredential, itemId: string, subId: string): Promise<JsonObject>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** One file of a zip archive, as its central directory lists it. */
|
|
2
|
+
export interface ZipEntry {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly isDirectory: boolean;
|
|
5
|
+
/** The uncompressed size the archive declares. */
|
|
6
|
+
readonly size: number;
|
|
7
|
+
/** The file's bytes (read on demand: a file is only inflated when it is wanted). */
|
|
8
|
+
read(): Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
/** Not a zip archive this reader understands (Python's `zipfile.is_zipfile` false, or a damaged archive). */
|
|
11
|
+
export declare class NotAZip extends Error {
|
|
12
|
+
readonly name = "NotAZip";
|
|
13
|
+
}
|
|
14
|
+
/** The files a zip archive lists, stored or deflated (the two methods a rule download uses). */
|
|
15
|
+
export declare function readZip(bytes: Uint8Array): ZipEntry[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The process environment, or a test's. */
|
|
2
|
+
export type Env = Readonly<Record<string, string | undefined>>;
|
|
3
|
+
/** Reads settings out of an environment, with the worker's rules: a typo stops the worker, it never picks a value. */
|
|
4
|
+
export declare class EnvReader {
|
|
5
|
+
readonly env: Env;
|
|
6
|
+
constructor(env: Env);
|
|
7
|
+
/** The raw value ("" unset). */
|
|
8
|
+
raw(name: string, fallback?: string): string;
|
|
9
|
+
/** The value without surrounding white space ("" unset). */
|
|
10
|
+
text(name: string, fallback?: string): string;
|
|
11
|
+
/** A value that must be set and not blank. */
|
|
12
|
+
required(name: string): string;
|
|
13
|
+
/** On or off; `fallback` when unset or empty; anything else stops the worker. */
|
|
14
|
+
switch(name: string, fallback: boolean): boolean;
|
|
15
|
+
/** On only for the words that mean on (the older switches, which read anything else as off). */
|
|
16
|
+
isOn(name: string): boolean;
|
|
17
|
+
/** A positive whole number; `fallback` when unset or empty; anything else stops the worker. */
|
|
18
|
+
positiveInt(name: string, fallback: number): number;
|
|
19
|
+
/** A whole number (Python's `int()` of the value: a sign, surrounding space and `_` between digits allowed). */
|
|
20
|
+
int(name: string, fallback: string): number;
|
|
21
|
+
/** A whole number, or null when unset or blank. */
|
|
22
|
+
optionalInt(name: string): number | null;
|
|
23
|
+
/** A comma separated list, blank entries dropped; `lower` lower-cases each entry. */
|
|
24
|
+
list(name: string, lower?: boolean): string[];
|
|
25
|
+
}
|
|
26
|
+
/** Python's `repr()` of a string, for an error message that quotes a value the way the Python worker did. */
|
|
27
|
+
export declare function pyRepr(value: string): string;
|
|
28
|
+
/** `value` without a trailing slash (Python's `rstrip("/")`). */
|
|
29
|
+
export declare function withoutTrailingSlash(value: string): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type SecretName, type SecretSource, type SecretsSourceName } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type Env } from "./env.js";
|
|
3
|
+
/** Where SECRETS_SOURCE=env finds each secret: an environment variable, or (the GitHub App's key) the file it names. */
|
|
4
|
+
export declare const ENV_NAMES: Readonly<Record<SecretName, string>>;
|
|
5
|
+
/** SECRETS_SOURCE: env (the default) or keyvault; a typo stops the worker. */
|
|
6
|
+
export declare function secretsSourceOf(env: Env): SecretsSourceName;
|
|
7
|
+
/**
|
|
8
|
+
* The secrets this configuration needs: the encryption keys always, the exchange's client secret when the exchange is
|
|
9
|
+
* on, the GitHub App's key when the feedback App is configured.
|
|
10
|
+
*/
|
|
11
|
+
export declare function wantedSecrets(env: Env): SecretName[];
|
|
12
|
+
/** SECRETS_SOURCE=env: the secrets from the environment (a Kubernetes Secret) and the mounted key file. */
|
|
13
|
+
export declare class EnvSecretSource implements SecretSource {
|
|
14
|
+
private readonly env;
|
|
15
|
+
private readonly readFile;
|
|
16
|
+
readonly name = "env";
|
|
17
|
+
constructor(env: Env, readFile?: (path: string) => string);
|
|
18
|
+
load(names: readonly SecretName[]): Promise<ReadonlyMap<SecretName, string>>;
|
|
19
|
+
private read;
|
|
20
|
+
private keyFile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SecretName, type Settings } from "@primusclaw/personal-agent";
|
|
2
|
+
import { type Env } from "./env.js";
|
|
3
|
+
/** The public client id of the GitHub CLI: an OAuth app most organizations already allow; its device flow needs no secret. */
|
|
4
|
+
export declare const GITHUB_CLI_CLIENT_ID = "178c6fc778ccc68e1d6a";
|
|
5
|
+
/** The default of ASSISTANT_REPLY_WINDOW_SECONDS. */
|
|
6
|
+
export declare const ASSISTANT_REPLY_WINDOW_SECONDS: number;
|
|
7
|
+
/**
|
|
8
|
+
* The worker's channel-neutral settings from the environment (the Python worker's `Settings.from_env`, without the
|
|
9
|
+
* Teams fields). `secrets` are what the deployment's SecretSource loaded; a name it holds is taken from there and never
|
|
10
|
+
* from the environment, and with SECRETS_SOURCE=keyvault a secret it does not hold stops the worker. Every malformed
|
|
11
|
+
* value stops it too (ConfigError), with the message the Python worker gave.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readSettings(env: Env, secrets?: ReadonlyMap<SecretName, string>): Settings;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The token is not one of these keys' (or is malformed, tampered with, or out of its ttl). */
|
|
2
|
+
export declare class InvalidToken extends Error {
|
|
3
|
+
readonly name = "InvalidToken";
|
|
4
|
+
}
|
|
5
|
+
export interface FernetOptions {
|
|
6
|
+
/** Wall-clock seconds, for the token's timestamp and a ttl check (default: the system clock). */
|
|
7
|
+
readonly nowSeconds?: () => number;
|
|
8
|
+
/** IV bytes (default: `node:crypto` randomBytes). */
|
|
9
|
+
readonly randomBytes?: (n: number) => Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
export declare class Fernet {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(key: string | Uint8Array, options?: FernetOptions);
|
|
14
|
+
/** A new key, as Python's `Fernet.generate_key()` gives it. */
|
|
15
|
+
static generateKey(): string;
|
|
16
|
+
/** The signing half of the key (for tests that check a derived key is not it). */
|
|
17
|
+
get signingKey(): Uint8Array;
|
|
18
|
+
encrypt(data: string | Uint8Array): string;
|
|
19
|
+
encryptAtTime(data: string | Uint8Array, currentTime: number): string;
|
|
20
|
+
/** The token for exactly these parts (the spec's generate vectors fix the IV). */
|
|
21
|
+
encryptFromParts(data: Uint8Array, currentTime: number, iv: Uint8Array): string;
|
|
22
|
+
/** The plaintext. Without `ttl` (how the store decrypts) the token's age is not checked. */
|
|
23
|
+
decrypt(token: string | Uint8Array, ttl?: number): Uint8Array;
|
|
24
|
+
decryptAtTime(token: string | Uint8Array, ttl: number, currentTime: number): Uint8Array;
|
|
25
|
+
/** @internal Shared with MultiFernet.rotate, which must not re-parse the token per key. */
|
|
26
|
+
decryptData(data: Uint8Array, timestamp: number, timeInfo?: readonly [number, number]): Uint8Array;
|
|
27
|
+
private verifySignature;
|
|
28
|
+
}
|
|
29
|
+
/** Encrypts with the first key, decrypts with any, and rotates a token to the first key keeping its timestamp. */
|
|
30
|
+
export declare class MultiFernet {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(fernets: readonly Fernet[]);
|
|
33
|
+
get primary(): Fernet;
|
|
34
|
+
encrypt(data: string | Uint8Array): string;
|
|
35
|
+
decrypt(token: string | Uint8Array, ttl?: number): Uint8Array;
|
|
36
|
+
rotate(token: string | Uint8Array, randomIv?: (n: number) => Uint8Array): string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The label the key is derived under. A new label is a new key: every pending code is void. */
|
|
2
|
+
export declare const LINK_HASH_LABEL = "primusclaw-teams/link-code-hmac/v1";
|
|
3
|
+
/** The HMAC key link codes are hashed under, derived from one Fernet key of TEAMS_ENCRYPTION_KEYS. */
|
|
4
|
+
export declare function linkHashKey(fernetKey: string): Uint8Array;
|
|
5
|
+
/** A link code as it is stored (hex). */
|
|
6
|
+
export declare function hashLinkCode(code: string, key: Uint8Array): string;
|