gsd-pi 2.3.6 → 2.3.8

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.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Remote Questions — durable prompt store
3
+ */
4
+
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { homedir } from "node:os";
8
+ import type { RemotePrompt, RemotePromptRecord, RemotePromptRef, RemoteAnswer, RemotePromptStatus } from "./types.js";
9
+
10
+ function runtimeDir(): string {
11
+ return join(homedir(), ".gsd", "runtime", "remote-questions");
12
+ }
13
+
14
+ function recordPath(id: string): string {
15
+ return join(runtimeDir(), `${id}.json`);
16
+ }
17
+
18
+ export function createPromptRecord(prompt: RemotePrompt): RemotePromptRecord {
19
+ return {
20
+ version: 1,
21
+ id: prompt.id,
22
+ createdAt: prompt.createdAt,
23
+ updatedAt: Date.now(),
24
+ status: "pending",
25
+ channel: prompt.channel,
26
+ timeoutAt: prompt.timeoutAt,
27
+ pollIntervalMs: prompt.pollIntervalMs,
28
+ questions: prompt.questions,
29
+ context: prompt.context,
30
+ };
31
+ }
32
+
33
+ export function writePromptRecord(record: RemotePromptRecord): void {
34
+ mkdirSync(runtimeDir(), { recursive: true });
35
+ writeFileSync(recordPath(record.id), JSON.stringify(record, null, 2) + "\n", "utf-8");
36
+ }
37
+
38
+ export function readPromptRecord(id: string): RemotePromptRecord | null {
39
+ const path = recordPath(id);
40
+ if (!existsSync(path)) return null;
41
+ try {
42
+ return JSON.parse(readFileSync(path, "utf-8")) as RemotePromptRecord;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ export function updatePromptRecord(
49
+ id: string,
50
+ updates: Partial<RemotePromptRecord>,
51
+ ): RemotePromptRecord | null {
52
+ const current = readPromptRecord(id);
53
+ if (!current) return null;
54
+ const next: RemotePromptRecord = {
55
+ ...current,
56
+ ...updates,
57
+ updatedAt: Date.now(),
58
+ };
59
+ writePromptRecord(next);
60
+ return next;
61
+ }
62
+
63
+ export function markPromptDispatched(id: string, ref: RemotePromptRef): RemotePromptRecord | null {
64
+ return updatePromptRecord(id, { ref, status: "pending" });
65
+ }
66
+
67
+ export function markPromptAnswered(id: string, response: RemoteAnswer): RemotePromptRecord | null {
68
+ return updatePromptRecord(id, { response, status: "answered", lastPollAt: Date.now() });
69
+ }
70
+
71
+ export function markPromptStatus(id: string, status: RemotePromptStatus, lastError?: string): RemotePromptRecord | null {
72
+ return updatePromptRecord(id, {
73
+ status,
74
+ lastPollAt: Date.now(),
75
+ ...(lastError ? { lastError } : {}),
76
+ });
77
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Remote Questions — shared types
3
+ */
4
+
5
+ export type RemoteChannel = "slack" | "discord";
6
+
7
+ export interface RemoteQuestionOption {
8
+ label: string;
9
+ description: string;
10
+ }
11
+
12
+ export interface RemoteQuestion {
13
+ id: string;
14
+ header: string;
15
+ question: string;
16
+ options: RemoteQuestionOption[];
17
+ allowMultiple: boolean;
18
+ }
19
+
20
+ export interface RemotePrompt {
21
+ id: string;
22
+ channel: RemoteChannel;
23
+ createdAt: number;
24
+ timeoutAt: number;
25
+ pollIntervalMs: number;
26
+ questions: RemoteQuestion[];
27
+ context?: {
28
+ source: string;
29
+ };
30
+ }
31
+
32
+ export interface RemotePromptRef {
33
+ id: string;
34
+ channel: RemoteChannel;
35
+ messageId: string;
36
+ channelId: string;
37
+ threadTs?: string;
38
+ threadUrl?: string;
39
+ }
40
+
41
+ export interface RemoteAnswer {
42
+ answers: Record<string, { answers: string[]; user_note?: string }>;
43
+ }
44
+
45
+ export type RemotePromptStatus = "pending" | "answered" | "timed_out" | "failed" | "cancelled";
46
+
47
+ export interface RemotePromptRecord {
48
+ version: 1;
49
+ id: string;
50
+ createdAt: number;
51
+ updatedAt: number;
52
+ status: RemotePromptStatus;
53
+ channel: RemoteChannel;
54
+ timeoutAt: number;
55
+ pollIntervalMs: number;
56
+ questions: RemoteQuestion[];
57
+ ref?: RemotePromptRef;
58
+ response?: RemoteAnswer;
59
+ lastPollAt?: number;
60
+ lastError?: string;
61
+ context?: {
62
+ source: string;
63
+ };
64
+ }
65
+
66
+ export interface RemoteDispatchResult {
67
+ ref: RemotePromptRef;
68
+ }
69
+
70
+ export interface ChannelAdapter {
71
+ readonly name: RemoteChannel;
72
+ validate(): Promise<void>;
73
+ sendPrompt(prompt: RemotePrompt): Promise<RemoteDispatchResult>;
74
+ pollAnswer(prompt: RemotePrompt, ref: RemotePromptRef): Promise<RemoteAnswer | null>;
75
+ }