@basou/sdk 0.9.0 → 0.11.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/index.d.ts CHANGED
@@ -114,7 +114,25 @@ interface Workspace {
114
114
  renderHandoff(): Promise<string>;
115
115
  /** The rendered `decisions.md` body (recomputed, without generated markers). */
116
116
  renderDecisions(): Promise<string>;
117
+ /**
118
+ * A rendered work report — a point-in-time markdown export explaining the
119
+ * work captured in this workspace (volume, decisions, approvals, tasks,
120
+ * changed files, and the local provenance integrity verdicts). Read-only,
121
+ * markerless. The CLI's `--json` structured shape is a CLI concern; the SDK
122
+ * facade returns the markdown body, mirroring `renderHandoff` / `renderDecisions`.
123
+ */
124
+ renderReport(options?: ReportOptions): Promise<string>;
117
125
  }
126
+ /** Options for {@link Workspace.renderReport}. */
127
+ type ReportOptions = {
128
+ /** Subject line shown in the report header. */
129
+ title?: string;
130
+ /**
131
+ * IANA timezone used to label the report's time figures. Defaults to the
132
+ * host's local zone (matching {@link StatsOptions.timeZone}).
133
+ */
134
+ timeZone?: string;
135
+ };
118
136
  /**
119
137
  * Resolve the Basou workspace root for a working directory by finding the
120
138
  * enclosing git repository root (`.basou/` lives at the repo root). A
@@ -155,9 +173,10 @@ declare function openWorkspace(repoRoot: string, options?: WorkspaceOptions): Pr
155
173
  */
156
174
  /**
157
175
  * SDK API version, tracking the Basou SDK surface (not the npm package
158
- * version, which moves in lockstep with the monorepo). `0.2.0` is the first
159
- * release with a runtime read API; `0.1.0` was types-only.
176
+ * version, which moves in lockstep with the monorepo). `0.2.0` was the first
177
+ * release with a runtime read API; `0.3.0` adds `Workspace.renderReport`;
178
+ * `0.1.0` was types-only.
160
179
  */
161
- declare const BASOU_SDK_VERSION = "0.2.0";
180
+ declare const BASOU_SDK_VERSION = "0.3.0";
162
181
 
163
- export { AmbiguousIdError, BASOU_SDK_VERSION, BasouSdkError, type StatsOptions, type Workspace, type WorkspaceDiagnostic, WorkspaceNotFoundError, type WorkspaceOptions, openWorkspace, resolveWorkspaceRoot };
182
+ export { AmbiguousIdError, BASOU_SDK_VERSION, BasouSdkError, type ReportOptions, type StatsOptions, type Workspace, type WorkspaceDiagnostic, WorkspaceNotFoundError, type WorkspaceOptions, openWorkspace, resolveWorkspaceRoot };
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  readTaskFileWithArchiveFallback,
40
40
  renderDecisions,
41
41
  renderHandoff,
42
+ renderReport,
42
43
  replayEvents,
43
44
  resolveRepositoryRoot,
44
45
  resolveSessionId,
@@ -143,6 +144,18 @@ async function openWorkspace(repoRoot, options = {}) {
143
144
  onSessionSkip: onSkip
144
145
  });
145
146
  return result.body;
147
+ },
148
+ renderReport: async (reportOptions) => {
149
+ const result = await renderReport({
150
+ paths,
151
+ nowIso: now().toISOString(),
152
+ ...reportOptions?.title !== void 0 ? { title: reportOptions.title } : {},
153
+ ...reportOptions?.timeZone !== void 0 ? { timeZone: reportOptions.timeZone } : {},
154
+ onWarning: (w, sid) => onWarning(w, sid),
155
+ onSessionSkip: onSkip,
156
+ onTaskSkip: onSkip
157
+ });
158
+ return result.body;
146
159
  }
147
160
  };
148
161
  }
@@ -162,7 +175,7 @@ async function resolveOrNull(resolver, input) {
162
175
  }
163
176
 
164
177
  // src/index.ts
165
- var BASOU_SDK_VERSION = "0.2.0";
178
+ var BASOU_SDK_VERSION = "0.3.0";
166
179
  export {
167
180
  AmbiguousIdError,
168
181
  BASOU_SDK_VERSION,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/workspace.ts","../src/index.ts"],"sourcesContent":["/**\n * Base class for every error the SDK throws on its own behalf. Errors that\n * originate in `@basou/core` (e.g. a malformed `session.yaml`) propagate as-is;\n * only the SDK's own preconditions are wrapped, so `instanceof BasouSdkError`\n * identifies \"the SDK rejected this call\" rather than \"the data was bad\".\n */\nexport class BasouSdkError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/**\n * `openWorkspace` was pointed at a path that is not a usable Basou workspace:\n * the `.basou/` directory is missing, is a symlink, or is otherwise not a\n * directory. The offending repository root is on {@link root}.\n */\nexport class WorkspaceNotFoundError extends BasouSdkError {\n readonly root: string;\n constructor(root: string, options?: { cause?: unknown }) {\n super(\n `No Basou workspace at ${root}: expected a '.basou/' directory (run 'basou init' there first).`,\n options,\n );\n this.root = root;\n }\n}\n\n/**\n * A session / task id prefix matched more than one record. The {@link input}\n * is the prefix as given; the caller should retry with a longer one. (A prefix\n * that matches nothing is NOT an error — the lookup returns `null` instead.)\n */\nexport class AmbiguousIdError extends BasouSdkError {\n readonly input: string;\n constructor(input: string, options?: { cause?: unknown }) {\n super(`Ambiguous id '${input}': matched more than one record; use a longer prefix.`, options);\n this.input = input;\n }\n}\n","import { join, resolve } from \"node:path\";\nimport {\n assertBasouRootSafe,\n basouPaths,\n buildStatusSnapshot,\n computeWorkStats,\n type Event,\n enumerateApprovals,\n type LoadedApproval,\n loadApproval,\n loadSessionEntries,\n loadTaskEntries,\n type Manifest,\n readAllEvents,\n readManifest,\n readTaskFileWithArchiveFallback,\n renderDecisions,\n renderHandoff,\n replayEvents,\n resolveRepositoryRoot,\n resolveSessionId,\n resolveTaskId,\n type SessionEntry,\n type StatusSnapshot,\n type TaskDocument,\n type WorkStatsResult,\n} from \"@basou/core\";\nimport { AmbiguousIdError, WorkspaceNotFoundError } from \"./errors.js\";\n\n/**\n * A degradation the SDK noticed while reading provenance: a malformed event\n * line, or a session / task that could not be loaded. Best-effort reads skip\n * these and keep going; pass `onDiagnostic` to {@link openWorkspace} to observe\n * them. `message` is a human-readable summary (it folds in the core\n * `ReplayWarning.kind` or skip-reason); structured fields are intentionally not\n * part of this stable shape.\n */\nexport type WorkspaceDiagnostic = {\n /** Human-readable summary of the malformed line / skipped record. */\n message: string;\n /** Session or task id the diagnostic relates to, when known. */\n id?: string;\n};\n\n/** Options for {@link openWorkspace}; all optional. */\nexport type WorkspaceOptions = {\n /**\n * Clock used for time-sensitive reads (session \"suspect\" classification,\n * stats span-to-now, status / approval expiry). Injectable for deterministic\n * callers and tests. Defaults to `() => new Date()`, evaluated per call.\n */\n now?: () => Date;\n /**\n * Observe a malformed event line or a skipped session / task instead of it\n * being silently dropped. Reads are still best-effort: a diagnostic does not\n * fail the call.\n */\n onDiagnostic?: (diagnostic: WorkspaceDiagnostic) => void;\n};\n\n/** Options for {@link Workspace.stats}. */\nexport type StatsOptions = {\n /**\n * IANA timezone used to bucket the per-day breakdown (native logs are UTC).\n * Defaults to the host's local zone.\n */\n timeZone?: string;\n};\n\n/**\n * A read-only handle on one Basou workspace (`<root>/.basou/`). Every method\n * reads provenance from disk; the SDK exposes no writers. Obtain one with\n * {@link openWorkspace}.\n *\n * Session / task lookups (`getSession`, `getTask`, `readEvents`,\n * `streamEvents`) accept a full id or a unique prefix: a prefix matching\n * nothing yields `null` (or an empty stream), a prefix matching more than one\n * record throws {@link AmbiguousIdError}. `getApproval` takes an exact id only.\n */\nexport interface Workspace {\n /** Absolute repository root this workspace was opened at. */\n readonly root: string;\n\n /** Parsed `manifest.yaml`. */\n manifest(): Promise<Manifest>;\n /** A freshly computed workspace status snapshot (directory presence + manifest). */\n status(): Promise<StatusSnapshot>;\n\n /** Every session, ULID-ascending, each with its `suspect` classification. */\n listSessions(): Promise<SessionEntry[]>;\n /** One session by id / unique prefix, or `null` if no session matches. */\n getSession(idOrPrefix: string): Promise<SessionEntry | null>;\n /** All events of a session, eagerly, ordered as written. Empty if no match. */\n readEvents(idOrPrefix: string): Promise<Event[]>;\n /** All events of a session as a lazy stream (for large logs). */\n streamEvents(idOrPrefix: string): AsyncIterable<Event>;\n\n /** Every task (active + lazily-indexed), created-at ascending. */\n listTasks(): Promise<TaskDocument[]>;\n /** One task by id / unique prefix (archived included), or `null`. */\n getTask(idOrPrefix: string): Promise<TaskDocument | null>;\n\n /** Pending + resolved approvals, fully loaded. */\n listApprovals(): Promise<{ pending: LoadedApproval[]; resolved: LoadedApproval[] }>;\n /** One approval by exact id (resolved checked first), or `null`. */\n getApproval(id: string): Promise<LoadedApproval | null>;\n\n /** Aggregated work / time / token stats across the workspace's sessions. */\n stats(options?: StatsOptions): Promise<WorkStatsResult>;\n\n /** The rendered `handoff.md` body (recomputed, without generated markers). */\n renderHandoff(): Promise<string>;\n /** The rendered `decisions.md` body (recomputed, without generated markers). */\n renderDecisions(): Promise<string>;\n}\n\n/**\n * Resolve the Basou workspace root for a working directory by finding the\n * enclosing git repository root (`.basou/` lives at the repo root). A\n * convenience for the common \"I'm somewhere in the repo\" case; requires git\n * and a repository. Pass the returned path to {@link openWorkspace}. When you\n * already know the root (CI checkout, a copied `.basou/`), skip this and call\n * {@link openWorkspace} directly — it needs no git.\n */\nexport function resolveWorkspaceRoot(cwd: string): Promise<string> {\n return resolveRepositoryRoot(cwd);\n}\n\n/**\n * Open a read-only handle on the Basou workspace rooted at `repoRoot` (the\n * directory that contains `.basou/`). Validates that `.basou/` exists and is a\n * real directory; throws {@link WorkspaceNotFoundError} otherwise. No git is\n * required — point it at any directory holding a `.basou/`.\n */\nexport async function openWorkspace(\n repoRoot: string,\n options: WorkspaceOptions = {},\n): Promise<Workspace> {\n // Normalize to an absolute path up front so `root` honors its documented\n // absolute-path contract even when the caller passes a relative directory.\n const root = resolve(repoRoot);\n const paths = basouPaths(root);\n try {\n await assertBasouRootSafe(paths.root);\n } catch (cause) {\n throw new WorkspaceNotFoundError(root, { cause });\n }\n const now = options.now ?? (() => new Date());\n const emit = options.onDiagnostic;\n const onWarning = (warning: { kind: string; line?: number }, id?: string): void =>\n emit?.({\n message: `event ${warning.kind}${warning.line ? ` (line ${warning.line})` : \"\"}`,\n ...(id !== undefined ? { id } : {}),\n });\n const onSkip = (id: string, reason: string): void =>\n emit?.({ message: `skipped: ${reason}`, id });\n\n /** Resolve a session prefix to a full id, or null when nothing matches. */\n const resolveSession = (input: string): Promise<string | null> =>\n resolveOrNull(() => resolveSessionId(paths, input), input);\n const resolveTask = (input: string): Promise<string | null> =>\n resolveOrNull(() => resolveTaskId(paths, input, { includeArchived: true }), input);\n\n return {\n root,\n\n manifest: () => readManifest(paths),\n\n status: async () =>\n buildStatusSnapshot({ manifest: await readManifest(paths), paths, now: now() }),\n\n listSessions: () =>\n loadSessionEntries(paths, {\n now: now(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSkip,\n }),\n\n getSession: async (idOrPrefix) => {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return null;\n const entries = await loadSessionEntries(paths, {\n now: now(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSkip,\n });\n return entries.find((e) => e.sessionId === id) ?? null;\n },\n\n readEvents: async (idOrPrefix) => {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return [];\n return readAllEvents(join(paths.sessions, id), { onWarning: (w) => onWarning(w, id) });\n },\n\n streamEvents: (idOrPrefix): AsyncIterable<Event> => {\n async function* iterate(): AsyncGenerator<Event> {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return;\n yield* replayEvents(join(paths.sessions, id), { onWarning: (w) => onWarning(w, id) });\n }\n return iterate();\n },\n\n listTasks: () => loadTaskEntries(paths, { onSkip }),\n\n getTask: async (idOrPrefix) => {\n const id = await resolveTask(idOrPrefix);\n if (id === null) return null;\n const { doc } = await readTaskFileWithArchiveFallback(paths, id);\n return doc;\n },\n\n listApprovals: async () => {\n const ids = await enumerateApprovals(paths);\n // `loadApproval` searches resolved/ before pending/, so an id present in\n // BOTH (a stale pending file left after resolution) would otherwise load\n // the resolved record into the pending list too. Drop those from pending\n // so a resolved approval is reported once, under `resolved`.\n const resolvedSet = new Set(ids.resolved);\n const pendingIds = ids.pending.filter((id) => !resolvedSet.has(id));\n const load = async (id: string): Promise<LoadedApproval | null> => loadApproval(paths, id);\n const [pending, resolved] = await Promise.all([\n Promise.all(pendingIds.map(load)),\n Promise.all(ids.resolved.map(load)),\n ]);\n return {\n pending: pending.filter((a): a is LoadedApproval => a !== null),\n resolved: resolved.filter((a): a is LoadedApproval => a !== null),\n };\n },\n\n getApproval: (id) => loadApproval(paths, id),\n\n stats: (statsOptions) =>\n computeWorkStats({\n paths,\n now: now(),\n ...(statsOptions?.timeZone !== undefined ? { timeZone: statsOptions.timeZone } : {}),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n }),\n\n renderHandoff: async () => {\n const result = await renderHandoff({\n paths,\n nowIso: now().toISOString(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n onTaskSkip: onSkip,\n });\n return result.body;\n },\n\n renderDecisions: async () => {\n const result = await renderDecisions({\n paths,\n nowIso: now().toISOString(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n });\n return result.body;\n },\n };\n}\n\n/**\n * Run a core id-resolver and normalize its outcome: a successful resolution\n * returns the id; the \"not found\" / \"empty input\" contract errors map to\n * `null` (no such record); the \"ambiguous\" contract error maps to\n * {@link AmbiguousIdError}. Any other error propagates unchanged.\n */\nasync function resolveOrNull(\n resolver: () => Promise<string>,\n input: string,\n): Promise<string | null> {\n try {\n return await resolver();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // Match the core resolver's exact contract strings (id-resolver.ts), not a\n // loose substring, so an unrelated error that merely contains \"not found\"\n // is never silently swallowed to null.\n if (/^Ambiguous (session|task) id /.test(message)) {\n throw new AmbiguousIdError(input, { cause: error });\n }\n if (\n /^(Session|Task) not found: /.test(message) ||\n /^(Session|Task) id is empty$/.test(message)\n ) {\n return null;\n }\n throw error;\n }\n}\n","/**\n * `@basou/sdk` — the stable, read-only programmatic API for reading a Basou\n * workspace's provenance (`.basou/`). It is a thin, ergonomic facade over\n * `@basou/core`'s readers: open a workspace once and query sessions, events,\n * tasks, approvals, status, stats, and the rendered handoff / decisions. No\n * writers are exposed — third-party tooling can read provenance without any\n * risk of mutating it.\n *\n * @example\n * ```ts\n * import { openWorkspace, resolveWorkspaceRoot } from \"@basou/sdk\";\n *\n * const root = await resolveWorkspaceRoot(process.cwd()); // or pass a known root\n * const ws = await openWorkspace(root);\n * for (const { session, suspect } of await ws.listSessions()) {\n * console.log(session.session.id, session.session.status, suspect);\n * }\n * const stats = await ws.stats();\n * console.log(stats.totals.billableActiveTimeMs);\n * ```\n */\n\n/**\n * SDK API version, tracking the Basou SDK surface (not the npm package\n * version, which moves in lockstep with the monorepo). `0.2.0` is the first\n * release with a runtime read API; `0.1.0` was types-only.\n */\nexport const BASOU_SDK_VERSION = \"0.2.0\";\n\n// Read types re-exported from @basou/core so consumers can type the values the\n// SDK returns without depending on @basou/core directly. These track the\n// on-disk provenance schema.\nexport type {\n ActiveTimeBasis,\n Approval,\n ApprovalStatus,\n CommandExecutedEvent,\n DayWorkStats,\n DecisionRecordedEvent,\n Event,\n FileChangedEvent,\n LoadedApproval,\n Manifest,\n MeasureAvailability,\n NoteAddedEvent,\n RiskLevel,\n Session,\n SessionEndedEvent,\n SessionEntry,\n SessionMetrics,\n SessionSourceKind,\n SessionStartedEvent,\n SessionStatus,\n SessionStatusChangedEvent,\n SessionWorkStats,\n SourceWorkStats,\n StatusCount,\n StatusSnapshot,\n SuspectReason,\n Task,\n TaskDocument,\n TaskStatus,\n TokenTotals,\n WorkStatsResult,\n WorkStatsTotals,\n} from \"@basou/core\";\nexport { AmbiguousIdError, BasouSdkError, WorkspaceNotFoundError } from \"./errors.js\";\nexport {\n openWorkspace,\n resolveWorkspaceRoot,\n type StatsOptions,\n type Workspace,\n type WorkspaceDiagnostic,\n type WorkspaceOptions,\n} from \"./workspace.js\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/workspace.ts","../src/index.ts"],"sourcesContent":["/**\n * Base class for every error the SDK throws on its own behalf. Errors that\n * originate in `@basou/core` (e.g. a malformed `session.yaml`) propagate as-is;\n * only the SDK's own preconditions are wrapped, so `instanceof BasouSdkError`\n * identifies \"the SDK rejected this call\" rather than \"the data was bad\".\n */\nexport class BasouSdkError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/**\n * `openWorkspace` was pointed at a path that is not a usable Basou workspace:\n * the `.basou/` directory is missing, is a symlink, or is otherwise not a\n * directory. The offending repository root is on {@link root}.\n */\nexport class WorkspaceNotFoundError extends BasouSdkError {\n readonly root: string;\n constructor(root: string, options?: { cause?: unknown }) {\n super(\n `No Basou workspace at ${root}: expected a '.basou/' directory (run 'basou init' there first).`,\n options,\n );\n this.root = root;\n }\n}\n\n/**\n * A session / task id prefix matched more than one record. The {@link input}\n * is the prefix as given; the caller should retry with a longer one. (A prefix\n * that matches nothing is NOT an error — the lookup returns `null` instead.)\n */\nexport class AmbiguousIdError extends BasouSdkError {\n readonly input: string;\n constructor(input: string, options?: { cause?: unknown }) {\n super(`Ambiguous id '${input}': matched more than one record; use a longer prefix.`, options);\n this.input = input;\n }\n}\n","import { join, resolve } from \"node:path\";\nimport {\n assertBasouRootSafe,\n basouPaths,\n buildStatusSnapshot,\n computeWorkStats,\n type Event,\n enumerateApprovals,\n type LoadedApproval,\n loadApproval,\n loadSessionEntries,\n loadTaskEntries,\n type Manifest,\n readAllEvents,\n readManifest,\n readTaskFileWithArchiveFallback,\n renderDecisions,\n renderHandoff,\n renderReport,\n replayEvents,\n resolveRepositoryRoot,\n resolveSessionId,\n resolveTaskId,\n type SessionEntry,\n type StatusSnapshot,\n type TaskDocument,\n type WorkStatsResult,\n} from \"@basou/core\";\nimport { AmbiguousIdError, WorkspaceNotFoundError } from \"./errors.js\";\n\n/**\n * A degradation the SDK noticed while reading provenance: a malformed event\n * line, or a session / task that could not be loaded. Best-effort reads skip\n * these and keep going; pass `onDiagnostic` to {@link openWorkspace} to observe\n * them. `message` is a human-readable summary (it folds in the core\n * `ReplayWarning.kind` or skip-reason); structured fields are intentionally not\n * part of this stable shape.\n */\nexport type WorkspaceDiagnostic = {\n /** Human-readable summary of the malformed line / skipped record. */\n message: string;\n /** Session or task id the diagnostic relates to, when known. */\n id?: string;\n};\n\n/** Options for {@link openWorkspace}; all optional. */\nexport type WorkspaceOptions = {\n /**\n * Clock used for time-sensitive reads (session \"suspect\" classification,\n * stats span-to-now, status / approval expiry). Injectable for deterministic\n * callers and tests. Defaults to `() => new Date()`, evaluated per call.\n */\n now?: () => Date;\n /**\n * Observe a malformed event line or a skipped session / task instead of it\n * being silently dropped. Reads are still best-effort: a diagnostic does not\n * fail the call.\n */\n onDiagnostic?: (diagnostic: WorkspaceDiagnostic) => void;\n};\n\n/** Options for {@link Workspace.stats}. */\nexport type StatsOptions = {\n /**\n * IANA timezone used to bucket the per-day breakdown (native logs are UTC).\n * Defaults to the host's local zone.\n */\n timeZone?: string;\n};\n\n/**\n * A read-only handle on one Basou workspace (`<root>/.basou/`). Every method\n * reads provenance from disk; the SDK exposes no writers. Obtain one with\n * {@link openWorkspace}.\n *\n * Session / task lookups (`getSession`, `getTask`, `readEvents`,\n * `streamEvents`) accept a full id or a unique prefix: a prefix matching\n * nothing yields `null` (or an empty stream), a prefix matching more than one\n * record throws {@link AmbiguousIdError}. `getApproval` takes an exact id only.\n */\nexport interface Workspace {\n /** Absolute repository root this workspace was opened at. */\n readonly root: string;\n\n /** Parsed `manifest.yaml`. */\n manifest(): Promise<Manifest>;\n /** A freshly computed workspace status snapshot (directory presence + manifest). */\n status(): Promise<StatusSnapshot>;\n\n /** Every session, ULID-ascending, each with its `suspect` classification. */\n listSessions(): Promise<SessionEntry[]>;\n /** One session by id / unique prefix, or `null` if no session matches. */\n getSession(idOrPrefix: string): Promise<SessionEntry | null>;\n /** All events of a session, eagerly, ordered as written. Empty if no match. */\n readEvents(idOrPrefix: string): Promise<Event[]>;\n /** All events of a session as a lazy stream (for large logs). */\n streamEvents(idOrPrefix: string): AsyncIterable<Event>;\n\n /** Every task (active + lazily-indexed), created-at ascending. */\n listTasks(): Promise<TaskDocument[]>;\n /** One task by id / unique prefix (archived included), or `null`. */\n getTask(idOrPrefix: string): Promise<TaskDocument | null>;\n\n /** Pending + resolved approvals, fully loaded. */\n listApprovals(): Promise<{ pending: LoadedApproval[]; resolved: LoadedApproval[] }>;\n /** One approval by exact id (resolved checked first), or `null`. */\n getApproval(id: string): Promise<LoadedApproval | null>;\n\n /** Aggregated work / time / token stats across the workspace's sessions. */\n stats(options?: StatsOptions): Promise<WorkStatsResult>;\n\n /** The rendered `handoff.md` body (recomputed, without generated markers). */\n renderHandoff(): Promise<string>;\n /** The rendered `decisions.md` body (recomputed, without generated markers). */\n renderDecisions(): Promise<string>;\n /**\n * A rendered work report — a point-in-time markdown export explaining the\n * work captured in this workspace (volume, decisions, approvals, tasks,\n * changed files, and the local provenance integrity verdicts). Read-only,\n * markerless. The CLI's `--json` structured shape is a CLI concern; the SDK\n * facade returns the markdown body, mirroring `renderHandoff` / `renderDecisions`.\n */\n renderReport(options?: ReportOptions): Promise<string>;\n}\n\n/** Options for {@link Workspace.renderReport}. */\nexport type ReportOptions = {\n /** Subject line shown in the report header. */\n title?: string;\n /**\n * IANA timezone used to label the report's time figures. Defaults to the\n * host's local zone (matching {@link StatsOptions.timeZone}).\n */\n timeZone?: string;\n};\n\n/**\n * Resolve the Basou workspace root for a working directory by finding the\n * enclosing git repository root (`.basou/` lives at the repo root). A\n * convenience for the common \"I'm somewhere in the repo\" case; requires git\n * and a repository. Pass the returned path to {@link openWorkspace}. When you\n * already know the root (CI checkout, a copied `.basou/`), skip this and call\n * {@link openWorkspace} directly — it needs no git.\n */\nexport function resolveWorkspaceRoot(cwd: string): Promise<string> {\n return resolveRepositoryRoot(cwd);\n}\n\n/**\n * Open a read-only handle on the Basou workspace rooted at `repoRoot` (the\n * directory that contains `.basou/`). Validates that `.basou/` exists and is a\n * real directory; throws {@link WorkspaceNotFoundError} otherwise. No git is\n * required — point it at any directory holding a `.basou/`.\n */\nexport async function openWorkspace(\n repoRoot: string,\n options: WorkspaceOptions = {},\n): Promise<Workspace> {\n // Normalize to an absolute path up front so `root` honors its documented\n // absolute-path contract even when the caller passes a relative directory.\n const root = resolve(repoRoot);\n const paths = basouPaths(root);\n try {\n await assertBasouRootSafe(paths.root);\n } catch (cause) {\n throw new WorkspaceNotFoundError(root, { cause });\n }\n const now = options.now ?? (() => new Date());\n const emit = options.onDiagnostic;\n const onWarning = (warning: { kind: string; line?: number }, id?: string): void =>\n emit?.({\n message: `event ${warning.kind}${warning.line ? ` (line ${warning.line})` : \"\"}`,\n ...(id !== undefined ? { id } : {}),\n });\n const onSkip = (id: string, reason: string): void =>\n emit?.({ message: `skipped: ${reason}`, id });\n\n /** Resolve a session prefix to a full id, or null when nothing matches. */\n const resolveSession = (input: string): Promise<string | null> =>\n resolveOrNull(() => resolveSessionId(paths, input), input);\n const resolveTask = (input: string): Promise<string | null> =>\n resolveOrNull(() => resolveTaskId(paths, input, { includeArchived: true }), input);\n\n return {\n root,\n\n manifest: () => readManifest(paths),\n\n status: async () =>\n buildStatusSnapshot({ manifest: await readManifest(paths), paths, now: now() }),\n\n listSessions: () =>\n loadSessionEntries(paths, {\n now: now(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSkip,\n }),\n\n getSession: async (idOrPrefix) => {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return null;\n const entries = await loadSessionEntries(paths, {\n now: now(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSkip,\n });\n return entries.find((e) => e.sessionId === id) ?? null;\n },\n\n readEvents: async (idOrPrefix) => {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return [];\n return readAllEvents(join(paths.sessions, id), { onWarning: (w) => onWarning(w, id) });\n },\n\n streamEvents: (idOrPrefix): AsyncIterable<Event> => {\n async function* iterate(): AsyncGenerator<Event> {\n const id = await resolveSession(idOrPrefix);\n if (id === null) return;\n yield* replayEvents(join(paths.sessions, id), { onWarning: (w) => onWarning(w, id) });\n }\n return iterate();\n },\n\n listTasks: () => loadTaskEntries(paths, { onSkip }),\n\n getTask: async (idOrPrefix) => {\n const id = await resolveTask(idOrPrefix);\n if (id === null) return null;\n const { doc } = await readTaskFileWithArchiveFallback(paths, id);\n return doc;\n },\n\n listApprovals: async () => {\n const ids = await enumerateApprovals(paths);\n // `loadApproval` searches resolved/ before pending/, so an id present in\n // BOTH (a stale pending file left after resolution) would otherwise load\n // the resolved record into the pending list too. Drop those from pending\n // so a resolved approval is reported once, under `resolved`.\n const resolvedSet = new Set(ids.resolved);\n const pendingIds = ids.pending.filter((id) => !resolvedSet.has(id));\n const load = async (id: string): Promise<LoadedApproval | null> => loadApproval(paths, id);\n const [pending, resolved] = await Promise.all([\n Promise.all(pendingIds.map(load)),\n Promise.all(ids.resolved.map(load)),\n ]);\n return {\n pending: pending.filter((a): a is LoadedApproval => a !== null),\n resolved: resolved.filter((a): a is LoadedApproval => a !== null),\n };\n },\n\n getApproval: (id) => loadApproval(paths, id),\n\n stats: (statsOptions) =>\n computeWorkStats({\n paths,\n now: now(),\n ...(statsOptions?.timeZone !== undefined ? { timeZone: statsOptions.timeZone } : {}),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n }),\n\n renderHandoff: async () => {\n const result = await renderHandoff({\n paths,\n nowIso: now().toISOString(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n onTaskSkip: onSkip,\n });\n return result.body;\n },\n\n renderDecisions: async () => {\n const result = await renderDecisions({\n paths,\n nowIso: now().toISOString(),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n });\n return result.body;\n },\n\n renderReport: async (reportOptions) => {\n const result = await renderReport({\n paths,\n nowIso: now().toISOString(),\n ...(reportOptions?.title !== undefined ? { title: reportOptions.title } : {}),\n ...(reportOptions?.timeZone !== undefined ? { timeZone: reportOptions.timeZone } : {}),\n onWarning: (w, sid) => onWarning(w, sid),\n onSessionSkip: onSkip,\n onTaskSkip: onSkip,\n });\n return result.body;\n },\n };\n}\n\n/**\n * Run a core id-resolver and normalize its outcome: a successful resolution\n * returns the id; the \"not found\" / \"empty input\" contract errors map to\n * `null` (no such record); the \"ambiguous\" contract error maps to\n * {@link AmbiguousIdError}. Any other error propagates unchanged.\n */\nasync function resolveOrNull(\n resolver: () => Promise<string>,\n input: string,\n): Promise<string | null> {\n try {\n return await resolver();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // Match the core resolver's exact contract strings (id-resolver.ts), not a\n // loose substring, so an unrelated error that merely contains \"not found\"\n // is never silently swallowed to null.\n if (/^Ambiguous (session|task) id /.test(message)) {\n throw new AmbiguousIdError(input, { cause: error });\n }\n if (\n /^(Session|Task) not found: /.test(message) ||\n /^(Session|Task) id is empty$/.test(message)\n ) {\n return null;\n }\n throw error;\n }\n}\n","/**\n * `@basou/sdk` — the stable, read-only programmatic API for reading a Basou\n * workspace's provenance (`.basou/`). It is a thin, ergonomic facade over\n * `@basou/core`'s readers: open a workspace once and query sessions, events,\n * tasks, approvals, status, stats, and the rendered handoff / decisions. No\n * writers are exposed — third-party tooling can read provenance without any\n * risk of mutating it.\n *\n * @example\n * ```ts\n * import { openWorkspace, resolveWorkspaceRoot } from \"@basou/sdk\";\n *\n * const root = await resolveWorkspaceRoot(process.cwd()); // or pass a known root\n * const ws = await openWorkspace(root);\n * for (const { session, suspect } of await ws.listSessions()) {\n * console.log(session.session.id, session.session.status, suspect);\n * }\n * const stats = await ws.stats();\n * console.log(stats.totals.billableActiveTimeMs);\n * ```\n */\n\n/**\n * SDK API version, tracking the Basou SDK surface (not the npm package\n * version, which moves in lockstep with the monorepo). `0.2.0` was the first\n * release with a runtime read API; `0.3.0` adds `Workspace.renderReport`;\n * `0.1.0` was types-only.\n */\nexport const BASOU_SDK_VERSION = \"0.3.0\";\n\n// Read types re-exported from @basou/core so consumers can type the values the\n// SDK returns without depending on @basou/core directly. These track the\n// on-disk provenance schema.\nexport type {\n ActiveTimeBasis,\n Approval,\n ApprovalStatus,\n CommandExecutedEvent,\n DayWorkStats,\n DecisionRecordedEvent,\n Event,\n FileChangedEvent,\n LoadedApproval,\n Manifest,\n MeasureAvailability,\n NoteAddedEvent,\n RiskLevel,\n Session,\n SessionEndedEvent,\n SessionEntry,\n SessionMetrics,\n SessionSourceKind,\n SessionStartedEvent,\n SessionStatus,\n SessionStatusChangedEvent,\n SessionWorkStats,\n SourceWorkStats,\n StatusCount,\n StatusSnapshot,\n SuspectReason,\n Task,\n TaskDocument,\n TaskStatus,\n TokenTotals,\n WorkStatsResult,\n WorkStatsTotals,\n} from \"@basou/core\";\nexport { AmbiguousIdError, BasouSdkError, WorkspaceNotFoundError } from \"./errors.js\";\nexport {\n openWorkspace,\n type ReportOptions,\n resolveWorkspaceRoot,\n type StatsOptions,\n type Workspace,\n type WorkspaceDiagnostic,\n type WorkspaceOptions,\n} from \"./workspace.js\";\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basou/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Read-only SDK for Basou: a stable, ergonomic API to read a workspace's provenance (sessions, events, tasks, approvals, stats).",
@@ -42,7 +42,7 @@
42
42
  "node": ">=20.10.0"
43
43
  },
44
44
  "dependencies": {
45
- "@basou/core": "0.9.0"
45
+ "@basou/core": "0.11.0"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsup",