@chenpengfei/daily-brief 0.1.1 → 0.1.3
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/CHANGELOG.md +43 -0
- package/README.md +1 -2
- package/dist/src/agent/daily-brief-agent.js +14 -0
- package/dist/src/agent/model-runtime-config.js +5 -77
- package/dist/src/cli.js +315 -252
- package/dist/src/config/credential-store.js +2 -14
- package/dist/src/config/model-config.js +8 -8
- package/dist/src/config/source-registry.js +30 -7
- package/dist/src/discord/delivery.js +4 -4
- package/dist/src/workflow/status.js +260 -21
- package/docs/operations.md +7 -17
- package/docs/user-manual.md +23 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes for Formal Releases are recorded here. GitHub Release notes should be derived from the matching version entry.
|
|
4
4
|
|
|
5
|
+
## 0.1.3 - 2026-06-06
|
|
6
|
+
|
|
7
|
+
Patch release for runtime configuration hardening and richer local status output.
|
|
8
|
+
|
|
9
|
+
### User-visible Changes
|
|
10
|
+
|
|
11
|
+
- Expands `daily-brief status` into a local readiness report with setup checks, today's run state, active paths, system timezone, and the next suggested action.
|
|
12
|
+
- Keeps model and Discord secrets in `auth.json` referenced by `config.yaml`, removing runtime reliance on `env:NAME`, `.env`, and `DISCORD_WEBHOOK_URL` credential paths for installed usage.
|
|
13
|
+
- Reports Discord delivery as explicitly skipped when delivery is disabled or the stored webhook credential is missing.
|
|
14
|
+
- Keeps `daily-brief run-once --date YYYY-MM-DD` available for manual backfills while making `daily-brief status` a no-flag inspection command.
|
|
15
|
+
|
|
16
|
+
### Installation and Upgrade Notes
|
|
17
|
+
|
|
18
|
+
- Upgrade with `npm install -g @chenpengfei/daily-brief@latest`.
|
|
19
|
+
- Run `daily-brief setup` after upgrading if your previous model or Discord configuration used environment-backed credential references.
|
|
20
|
+
- Scripted environments should write `config.yaml`, `sources.yaml`, and `auth.json` directly under `DAILY_BRIEF_HOME`; use `DAILY_BRIEF_DATA_HOME` when generated data should live elsewhere.
|
|
21
|
+
|
|
22
|
+
### Known Limitations
|
|
23
|
+
|
|
24
|
+
- `daily-brief status` is read-only and local: it does not collect Sources, call an LLM, refresh credentials, generate a brief, or send Discord notifications.
|
|
25
|
+
- Environment variables remain limited to path overrides for installed usage: `DAILY_BRIEF_HOME` and `DAILY_BRIEF_DATA_HOME`.
|
|
26
|
+
|
|
27
|
+
## 0.1.2 - 2026-06-04
|
|
28
|
+
|
|
29
|
+
Patch release for the simplified public CLI setup workflow.
|
|
30
|
+
|
|
31
|
+
### User-visible Changes
|
|
32
|
+
|
|
33
|
+
- Simplifies the public CLI around `setup`, `run-once`, `status`, `sources`, `version`, and help.
|
|
34
|
+
- Reworks `daily-brief setup` into an interactive wizard that preserves existing files by default and guides model credential and optional Discord delivery configuration.
|
|
35
|
+
- Adds human-readable `run-once` progress output and clearer Source listing/edit guidance.
|
|
36
|
+
- Makes `delivery.enabled: false` take precedence over `DISCORD_WEBHOOK_URL`, preventing accidental Discord sends when delivery is disabled in config.
|
|
37
|
+
|
|
38
|
+
### Installation and Upgrade Notes
|
|
39
|
+
|
|
40
|
+
- Upgrade with `npm install -g @chenpengfei/daily-brief@latest`.
|
|
41
|
+
- Run `daily-brief setup` again after upgrading to review the simplified setup flow.
|
|
42
|
+
- Non-interactive setup now fails before writing configuration files; scripted environments should write `config.yaml`, `sources.yaml`, and `auth.json` directly.
|
|
43
|
+
|
|
44
|
+
### Known Limitations
|
|
45
|
+
|
|
46
|
+
- The simplified public CLI removes direct public `collect`, `generate`, `deliver`, `model`, and `delivery` command entry points. Use `setup`, `run-once`, `status`, and `sources` for normal installed usage.
|
|
47
|
+
|
|
5
48
|
## 0.1.1 - 2026-06-01
|
|
6
49
|
|
|
7
50
|
Patch release for first-install command discovery.
|
package/README.md
CHANGED
|
@@ -25,8 +25,7 @@ Daily Brief requires Node.js 22 or newer.
|
|
|
25
25
|
daily-brief run-once
|
|
26
26
|
daily-brief status
|
|
27
27
|
daily-brief sources list
|
|
28
|
-
daily-brief
|
|
29
|
-
daily-brief delivery status
|
|
28
|
+
daily-brief version
|
|
30
29
|
```
|
|
31
30
|
|
|
32
31
|
For installation, setup, configuration, upgrade, and troubleshooting, see `docs/user-manual.md`.
|
|
@@ -17,6 +17,7 @@ export async function runOnce(options = {}) {
|
|
|
17
17
|
const dateKey = options.dateKey;
|
|
18
18
|
let collection;
|
|
19
19
|
try {
|
|
20
|
+
options.onProgress?.("1/5 Collecting Source Items");
|
|
20
21
|
collection = await collectSources({
|
|
21
22
|
date,
|
|
22
23
|
...(dateKey ? { dateKey } : {}),
|
|
@@ -24,6 +25,9 @@ export async function runOnce(options = {}) {
|
|
|
24
25
|
...(options.sourceRegistryPath ? { sourceRegistryPath: options.sourceRegistryPath } : {}),
|
|
25
26
|
...(options.sourceItemRoot ? { sourceItemRoot: options.sourceItemRoot } : {})
|
|
26
27
|
});
|
|
28
|
+
for (const source of collection.sources) {
|
|
29
|
+
options.onProgress?.(`- ${source.sourceId}: ${source.status}, items ${source.itemCount}, written ${source.writtenCount}, duplicates ${source.skippedDuplicateCount}${source.reason ? `, ${source.reason}` : ""}`);
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
catch (error) {
|
|
29
33
|
const coreFailure = {
|
|
@@ -32,6 +36,7 @@ export async function runOnce(options = {}) {
|
|
|
32
36
|
};
|
|
33
37
|
const delivery = await deliverCoreFailureNotification(coreFailure, {
|
|
34
38
|
...(options.discordWebhookUrl ? { webhookUrl: options.discordWebhookUrl } : {}),
|
|
39
|
+
...(options.discordEnv ? { env: options.discordEnv } : {}),
|
|
35
40
|
...(options.discordFetchImpl ? { fetchImpl: options.discordFetchImpl } : {})
|
|
36
41
|
});
|
|
37
42
|
return {
|
|
@@ -50,6 +55,7 @@ export async function runOnce(options = {}) {
|
|
|
50
55
|
if (collectionFailure) {
|
|
51
56
|
const delivery = await deliverCoreFailureNotification(collectionFailure, {
|
|
52
57
|
...(options.discordWebhookUrl ? { webhookUrl: options.discordWebhookUrl } : {}),
|
|
58
|
+
...(options.discordEnv ? { env: options.discordEnv } : {}),
|
|
53
59
|
...(options.discordFetchImpl ? { fetchImpl: options.discordFetchImpl } : {})
|
|
54
60
|
});
|
|
55
61
|
return {
|
|
@@ -64,6 +70,7 @@ export async function runOnce(options = {}) {
|
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
72
|
const collectionFailures = collectionFailureRefs(collection);
|
|
73
|
+
options.onProgress?.("2/5 Generating Daily Brief");
|
|
67
74
|
const generated = await generateOnce({
|
|
68
75
|
...options,
|
|
69
76
|
...(dateKey ? { dateKey } : {}),
|
|
@@ -75,6 +82,7 @@ export async function runOnce(options = {}) {
|
|
|
75
82
|
}
|
|
76
83
|
: {})
|
|
77
84
|
});
|
|
85
|
+
options.onProgress?.("4/5 Delivering Notification");
|
|
78
86
|
const delivery = await deliverOnce({
|
|
79
87
|
...options,
|
|
80
88
|
date,
|
|
@@ -124,6 +132,7 @@ export async function generateOnce(options = {}) {
|
|
|
124
132
|
const collectionInputRefs = options.collectionFailures
|
|
125
133
|
? { collectionFailures: options.collectionFailures }
|
|
126
134
|
: undefined;
|
|
135
|
+
options.onProgress?.("- Understanding Source Items");
|
|
127
136
|
const understanding = await runSourceItemUnderstandingStage({
|
|
128
137
|
sourceItems,
|
|
129
138
|
modelRuntimeConfig,
|
|
@@ -131,6 +140,7 @@ export async function generateOnce(options = {}) {
|
|
|
131
140
|
...(collectionInputRefs ? { inputRefs: collectionInputRefs } : {}),
|
|
132
141
|
...(options.modelRuntimeEnv ? { modelRuntimeEnv: options.modelRuntimeEnv } : {})
|
|
133
142
|
});
|
|
143
|
+
options.onProgress?.("- Selecting and Ranking Signals");
|
|
134
144
|
const selected = await runSignalSelectionAndRankingStages({
|
|
135
145
|
sourceItems,
|
|
136
146
|
annotations: understanding.annotations,
|
|
@@ -147,6 +157,7 @@ export async function generateOnce(options = {}) {
|
|
|
147
157
|
signals: selected.signals
|
|
148
158
|
};
|
|
149
159
|
const narrativeEvents = [];
|
|
160
|
+
options.onProgress?.("- Writing Narrative");
|
|
150
161
|
const narrative = await enrichDailyBriefNarrativeWithAgent({
|
|
151
162
|
brief: selectedBrief,
|
|
152
163
|
sourceItems,
|
|
@@ -161,6 +172,7 @@ export async function generateOnce(options = {}) {
|
|
|
161
172
|
brief: narrative.brief,
|
|
162
173
|
...(options.collectionFailures ? { collectionFailures: options.collectionFailures } : {})
|
|
163
174
|
});
|
|
175
|
+
options.onProgress?.("- Checking Source Grounding");
|
|
164
176
|
const audited = await auditBriefWithSingleRepairAttempt({
|
|
165
177
|
narrativeBrief: narrative.brief,
|
|
166
178
|
sourceItems,
|
|
@@ -175,6 +187,7 @@ export async function generateOnce(options = {}) {
|
|
|
175
187
|
const writtenArtifact = options.agentRunRoot ? await writeAgentRunArtifact(artifact, date, options.agentRunRoot, dateKey) : undefined;
|
|
176
188
|
const markdown = renderDailyBriefMarkdown(audited.brief);
|
|
177
189
|
const piResult = await renderBriefThroughPiRuntime(markdown);
|
|
190
|
+
options.onProgress?.("3/5 Archiving Daily Brief");
|
|
178
191
|
const archived = await writeBriefArchive(piResult.markdown, date, options.archiveRoot, dateKey);
|
|
179
192
|
return {
|
|
180
193
|
archivePath: archived.path,
|
|
@@ -309,6 +322,7 @@ export async function deliverOnce(options = {}) {
|
|
|
309
322
|
...(options.discordTemplatePath ? { templatePath: options.discordTemplatePath } : {})
|
|
310
323
|
}, {
|
|
311
324
|
...(options.discordWebhookUrl ? { webhookUrl: options.discordWebhookUrl } : {}),
|
|
325
|
+
...(options.discordEnv ? { env: options.discordEnv } : {}),
|
|
312
326
|
...(options.discordFetchImpl ? { fetchImpl: options.discordFetchImpl } : {})
|
|
313
327
|
});
|
|
314
328
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { getOAuthApiKey, loginOpenAICodex } from "@earendil-works/pi-ai/oauth";
|
|
3
|
-
import {
|
|
3
|
+
import { getCredential, putCredential, readCredentialStore, readModelConfig, removeCredential, writeCredentialStore } from "../config/index.js";
|
|
4
4
|
import { resolveDailyBriefPaths } from "../config/paths.js";
|
|
5
5
|
export const defaultOAuthHelpers = {
|
|
6
6
|
getOAuthApiKey,
|
|
@@ -10,17 +10,17 @@ export function readModelRuntimeConfig(env = process.env, options = {}) {
|
|
|
10
10
|
const paths = resolveDailyBriefPaths(env);
|
|
11
11
|
const configPath = options.configPath ?? paths.configPath;
|
|
12
12
|
const authPath = options.authPath ?? paths.authPath;
|
|
13
|
-
const config =
|
|
13
|
+
const config = existsSync(configPath) ? readModelConfig(configPath) : undefined;
|
|
14
14
|
if (!config) {
|
|
15
15
|
return {
|
|
16
16
|
provider: "openai-codex",
|
|
17
17
|
model: "gpt-5.5",
|
|
18
18
|
credentialRef: "openai-codex.default",
|
|
19
19
|
ready: false,
|
|
20
|
-
issues: [`Model config not found: ${configPath}. Run daily-brief setup
|
|
20
|
+
issues: [`Model config not found: ${configPath}. Run daily-brief setup or create config.yaml with model settings.`]
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
const issues = credentialIssues(config.provider, config.credentialRef,
|
|
23
|
+
const issues = credentialIssues(config.provider, config.credentialRef, authPath);
|
|
24
24
|
return {
|
|
25
25
|
provider: config.provider,
|
|
26
26
|
model: config.model,
|
|
@@ -38,10 +38,6 @@ export async function resolveModelApiKey(config, env = process.env, options = {}
|
|
|
38
38
|
if (!credentialRef) {
|
|
39
39
|
throw new Error(`credentialRef is required for ${config.provider}`);
|
|
40
40
|
}
|
|
41
|
-
if (isEnvCredentialRef(credentialRef)) {
|
|
42
|
-
const envName = envNameFromCredentialRef(credentialRef);
|
|
43
|
-
return env[envName]?.trim();
|
|
44
|
-
}
|
|
45
41
|
const authPath = options.authPath ?? resolveDailyBriefPaths(env).authPath;
|
|
46
42
|
const credential = getCredential(credentialRef, authPath);
|
|
47
43
|
if (!credential) {
|
|
@@ -108,17 +104,13 @@ export function statusModelCredentials(env = process.env, options = {}) {
|
|
|
108
104
|
secret: "<redacted>"
|
|
109
105
|
}));
|
|
110
106
|
}
|
|
111
|
-
function credentialIssues(provider, credentialRef,
|
|
107
|
+
function credentialIssues(provider, credentialRef, authPath) {
|
|
112
108
|
if (provider === "faux") {
|
|
113
109
|
return [];
|
|
114
110
|
}
|
|
115
111
|
if (!credentialRef) {
|
|
116
112
|
return [`credentialRef is required for ${provider}`];
|
|
117
113
|
}
|
|
118
|
-
if (isEnvCredentialRef(credentialRef)) {
|
|
119
|
-
const envName = envNameFromCredentialRef(credentialRef);
|
|
120
|
-
return env[envName]?.trim() ? [] : [`${envName} is required for credentialRef ${credentialRef}`];
|
|
121
|
-
}
|
|
122
114
|
const credential = getCredential(credentialRef, authPath);
|
|
123
115
|
if (!credential) {
|
|
124
116
|
return [`Credential not found: ${credentialRef}`];
|
|
@@ -128,65 +120,6 @@ function credentialIssues(provider, credentialRef, env, authPath) {
|
|
|
128
120
|
}
|
|
129
121
|
return [];
|
|
130
122
|
}
|
|
131
|
-
function readEnvModelConfig(env) {
|
|
132
|
-
const provider = env.DAILY_BRIEF_MODEL_PROVIDER?.trim();
|
|
133
|
-
if (!provider) {
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
const normalizedProvider = normalizeProvider(provider, env);
|
|
137
|
-
return {
|
|
138
|
-
provider: normalizedProvider,
|
|
139
|
-
model: env.DAILY_BRIEF_MODEL?.trim() || defaultModelForProvider(normalizedProvider),
|
|
140
|
-
...(env.DAILY_BRIEF_MODEL_BASE_URL?.trim() ? { baseUrl: env.DAILY_BRIEF_MODEL_BASE_URL.trim() } : {}),
|
|
141
|
-
...readEnvCredentialRef(env, normalizedProvider)
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
function normalizeProvider(value, env) {
|
|
145
|
-
const provider = value.trim().toLowerCase();
|
|
146
|
-
if (provider === "codex" || provider === "hermes") {
|
|
147
|
-
return "openai-codex";
|
|
148
|
-
}
|
|
149
|
-
if (provider === "faux") {
|
|
150
|
-
if (env.DAILY_BRIEF_ALLOW_FAUX_PROVIDER === "true") {
|
|
151
|
-
return provider;
|
|
152
|
-
}
|
|
153
|
-
throw new Error("DAILY_BRIEF_MODEL_PROVIDER=faux is only allowed when DAILY_BRIEF_ALLOW_FAUX_PROVIDER=true");
|
|
154
|
-
}
|
|
155
|
-
if (provider === "openai-codex" || provider === "openai" || provider === "deepseek" || provider === "openai-compatible") {
|
|
156
|
-
return provider;
|
|
157
|
-
}
|
|
158
|
-
throw new Error(`Unsupported DAILY_BRIEF_MODEL_PROVIDER: ${value}`);
|
|
159
|
-
}
|
|
160
|
-
function defaultModelForProvider(provider) {
|
|
161
|
-
if (provider === "openai-codex") {
|
|
162
|
-
return "gpt-5.5";
|
|
163
|
-
}
|
|
164
|
-
if (provider === "openai") {
|
|
165
|
-
return "gpt-4.1-mini";
|
|
166
|
-
}
|
|
167
|
-
if (provider === "deepseek") {
|
|
168
|
-
return "deepseek-chat";
|
|
169
|
-
}
|
|
170
|
-
if (provider === "openai-compatible") {
|
|
171
|
-
return "openai-compatible-model";
|
|
172
|
-
}
|
|
173
|
-
return "faux-daily-brief-renderer";
|
|
174
|
-
}
|
|
175
|
-
function defaultCredentialRefForProvider(provider) {
|
|
176
|
-
if (provider === "faux") {
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
if (provider === "openai") {
|
|
180
|
-
return "env:OPENAI_API_KEY";
|
|
181
|
-
}
|
|
182
|
-
if (provider === "deepseek") {
|
|
183
|
-
return "env:DEEPSEEK_API_KEY";
|
|
184
|
-
}
|
|
185
|
-
if (provider === "openai-compatible") {
|
|
186
|
-
return "env:OPENAI_API_KEY";
|
|
187
|
-
}
|
|
188
|
-
return "openai-codex.default";
|
|
189
|
-
}
|
|
190
123
|
function persistRefreshedOAuthCredential(ref, credential, credentials, authPath) {
|
|
191
124
|
const store = readCredentialStore(authPath);
|
|
192
125
|
const previous = store.credentials[ref];
|
|
@@ -198,11 +131,6 @@ function persistRefreshedOAuthCredential(ref, credential, credentials, authPath)
|
|
|
198
131
|
};
|
|
199
132
|
writeCredentialStore(store, authPath);
|
|
200
133
|
}
|
|
201
|
-
function readEnvCredentialRef(env, provider) {
|
|
202
|
-
const credentialRef = env.DAILY_BRIEF_MODEL_CREDENTIAL_REF?.trim() ||
|
|
203
|
-
defaultCredentialRefForProvider(provider);
|
|
204
|
-
return credentialRef ? { credentialRef } : {};
|
|
205
|
-
}
|
|
206
134
|
function promptForOAuth(io, message) {
|
|
207
135
|
if (!io.prompt) {
|
|
208
136
|
throw new Error(`OAuth login requires interactive input: ${message}`);
|