@phamvuhoang/otto-core 0.19.0 → 0.20.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 +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/journal-config.d.ts +9 -0
- package/dist/journal-config.d.ts.map +1 -0
- package/dist/journal-config.js +31 -0
- package/dist/journal-config.js.map +1 -0
- package/dist/journal-gate.d.ts +46 -0
- package/dist/journal-gate.d.ts.map +1 -0
- package/dist/journal-gate.js +135 -0
- package/dist/journal-gate.js.map +1 -0
- package/dist/journal-ledger.d.ts +13 -0
- package/dist/journal-ledger.d.ts.map +1 -0
- package/dist/journal-ledger.js +33 -0
- package/dist/journal-ledger.js.map +1 -0
- package/dist/journal-source.d.ts +19 -0
- package/dist/journal-source.d.ts.map +1 -0
- package/dist/journal-source.js +38 -0
- package/dist/journal-source.js.map +1 -0
- package/dist/journal.d.ts +46 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +158 -0
- package/dist/journal.js.map +1 -0
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +19 -0
- package/dist/loop.js.map +1 -1
- package/dist/run-bin.js +1 -1
- package/dist/run-bin.js.map +1 -1
- package/dist/stages.d.ts +10 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +13 -0
- package/dist/stages.js.map +1 -1
- package/dist/threads-api.d.ts +46 -0
- package/dist/threads-api.d.ts.map +1 -0
- package/dist/threads-api.js +101 -0
- package/dist/threads-api.js.map +1 -0
- package/package.json +1 -1
- package/templates/journal-screen.md +19 -0
- package/templates/journal-write.md +16 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads (Meta) publishing client for the P12 public journal (issue #67).
|
|
3
|
+
* Mirrors linear-api.ts: injectable fetch + credentials from env or
|
|
4
|
+
* ~/.config/otto/threads.json. The Threads Graph API publishes in two steps —
|
|
5
|
+
* create a TEXT container, then publish it.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
const defaultAuthDeps = {
|
|
11
|
+
env: process.env,
|
|
12
|
+
readFile: (p) => {
|
|
13
|
+
try {
|
|
14
|
+
return readFileSync(p, "utf8");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
home: homedir(),
|
|
21
|
+
};
|
|
22
|
+
/** Canonical location of the stored Threads credentials (outside any repo). */
|
|
23
|
+
export function threadsConfigPath(home) {
|
|
24
|
+
return join(home, ".config", "otto", "threads.json");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a Threads token + user id with precedence `OTTO_THREADS_TOKEN` +
|
|
28
|
+
* `OTTO_THREADS_USER_ID` → `~/.config/otto/threads.json` (`{ token, userId }`).
|
|
29
|
+
* Returns null when no source yields both a token and a user id.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveThreadsAuth(deps = defaultAuthDeps) {
|
|
32
|
+
const token = deps.env.OTTO_THREADS_TOKEN?.trim();
|
|
33
|
+
const userId = deps.env.OTTO_THREADS_USER_ID?.trim();
|
|
34
|
+
if (token && userId)
|
|
35
|
+
return { token, userId, source: "env" };
|
|
36
|
+
const path = threadsConfigPath(deps.home);
|
|
37
|
+
const raw = deps.readFile(path);
|
|
38
|
+
if (raw != null) {
|
|
39
|
+
try {
|
|
40
|
+
const o = JSON.parse(raw);
|
|
41
|
+
if (typeof o.token === "string" &&
|
|
42
|
+
o.token.trim() &&
|
|
43
|
+
typeof o.userId === "string" &&
|
|
44
|
+
o.userId.trim()) {
|
|
45
|
+
return { token: o.token.trim(), userId: o.userId.trim(), source: path };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// malformed → no credential from this source
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
export class ThreadsApiError extends Error {
|
|
55
|
+
kind;
|
|
56
|
+
constructor(message, kind) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "ThreadsApiError";
|
|
59
|
+
this.kind = kind;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const DEFAULT_BASE = "https://graph.threads.net/v1.0";
|
|
63
|
+
/**
|
|
64
|
+
* Create a Threads client. The two-step publish: create a TEXT container
|
|
65
|
+
* (`/{userId}/threads`), then publish it (`/{userId}/threads_publish`). `fetch`
|
|
66
|
+
* is injectable for tests; errors are classified as auth/network/api.
|
|
67
|
+
*/
|
|
68
|
+
export function createThreadsClient(opts) {
|
|
69
|
+
const fetchImpl = opts.fetch ?? fetch;
|
|
70
|
+
const base = opts.baseUrl ?? DEFAULT_BASE;
|
|
71
|
+
if (!opts.token || !opts.userId) {
|
|
72
|
+
throw new ThreadsApiError("missing Threads credentials", "auth");
|
|
73
|
+
}
|
|
74
|
+
const post = async (url) => {
|
|
75
|
+
let res;
|
|
76
|
+
try {
|
|
77
|
+
res = await fetchImpl(url, { method: "POST" });
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
throw new ThreadsApiError(`Threads request failed: ${e.message}`, "network");
|
|
81
|
+
}
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
throw new ThreadsApiError(`Threads API returned ${res.status}`, "api");
|
|
84
|
+
}
|
|
85
|
+
const json = (await res.json());
|
|
86
|
+
if (typeof json.id !== "string") {
|
|
87
|
+
throw new ThreadsApiError("Threads API response missing id", "api");
|
|
88
|
+
}
|
|
89
|
+
return { id: json.id };
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
async publish(text) {
|
|
93
|
+
const tok = encodeURIComponent(opts.token);
|
|
94
|
+
const create = `${base}/${opts.userId}/threads?media_type=TEXT&text=${encodeURIComponent(text)}&access_token=${tok}`;
|
|
95
|
+
const { id: creationId } = await post(create);
|
|
96
|
+
const publish = `${base}/${opts.userId}/threads_publish?creation_id=${encodeURIComponent(creationId)}&access_token=${tok}`;
|
|
97
|
+
return post(publish);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=threads-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads-api.js","sourceRoot":"","sources":["../src/threads-api.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
You are a strict pre-publication secrecy reviewer. A note is about to be posted
|
|
2
|
+
publicly by an autonomous coding agent. Your ONLY job is to prevent leaks.
|
|
3
|
+
|
|
4
|
+
Decide whether the note below is SAFE to post. Treat it as UNSAFE if a reader
|
|
5
|
+
could identify the specific repository, product, company, person, or customer —
|
|
6
|
+
or if it contains code, secrets, paths, URLs, names, or any private/internal
|
|
7
|
+
detail. If there is ANY doubt whatsoever, answer UNSAFE.
|
|
8
|
+
|
|
9
|
+
Respond with EXACTLY one line and nothing else:
|
|
10
|
+
|
|
11
|
+
<journal-verdict>SAFE</journal-verdict>
|
|
12
|
+
|
|
13
|
+
or
|
|
14
|
+
|
|
15
|
+
<journal-verdict>UNSAFE</journal-verdict>
|
|
16
|
+
|
|
17
|
+
<note>
|
|
18
|
+
{{ INPUTS }}
|
|
19
|
+
</note>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You are _a coding agent's field notes_ — you share short, generic lessons about
|
|
2
|
+
the craft of software work, never about any specific project.
|
|
3
|
+
|
|
4
|
+
Below is one durable learning. Rewrite it as a SINGLE first-person field note
|
|
5
|
+
(2–4 sentences, under 400 characters) capturing the GENERAL craft lesson.
|
|
6
|
+
|
|
7
|
+
STRICT RULES:
|
|
8
|
+
|
|
9
|
+
- No project, product, company, person, file, path, tool, version, or ticket names.
|
|
10
|
+
- No code, commands, URLs, or identifiers of any kind.
|
|
11
|
+
- If the learning cannot be generalized without specifics, output exactly: SKIP
|
|
12
|
+
- Output ONLY the note text (or SKIP). No preamble, no quotes, no markdown.
|
|
13
|
+
|
|
14
|
+
<learning>
|
|
15
|
+
{{ INPUTS }}
|
|
16
|
+
</learning>
|