@agentshouse/kit 0.1.0-alpha.3 → 0.1.0-alpha.31
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/README.md +722 -263
- package/bin/connect-linux.sh +200 -25
- package/bin/kit.js +179 -157
- package/bootstrap.json +17 -0
- package/package.json +7 -2
- package/src/adapter-mcp.js +76 -12
- package/src/agents.js +63 -8
- package/src/binding.js +52 -0
- package/src/blocks.js +2 -26
- package/src/bridge.js +28 -14
- package/src/bytes.js +19 -8
- package/src/configuration.js +13 -209
- package/src/connector-login.js +3 -84
- package/src/contract.js +36 -11
- package/src/delivery.js +4 -7
- package/src/directory.js +6 -26
- package/src/draft.js +4 -82
- package/src/git-main.js +138 -0
- package/src/git.js +62 -0
- package/src/google/calendar.js +39 -45
- package/src/google/drive.js +61 -186
- package/src/google/entries.js +346 -0
- package/src/google/gateway.js +3 -6
- package/src/google/gmail.js +33 -23
- package/src/google/oauth.js +106 -66
- package/src/google/source.js +21 -23
- package/src/host.js +6 -2
- package/src/house-main.js +65 -8
- package/src/house.js +446 -127
- package/src/how-we-work.md +25 -0
- package/src/inbound.js +2 -18
- package/src/kit-work-stream.js +118 -0
- package/src/launcher.js +46 -10
- package/src/local-files.js +3 -11
- package/src/login.js +23 -11
- package/src/mcp.js +18 -0
- package/src/mount.js +13 -21
- package/src/original.js +4 -4
- package/src/pacing.js +41 -0
- package/src/plan.js +37 -45
- package/src/repository.js +3 -61
- package/src/session-bin/git +2 -0
- package/src/setup.js +14 -15
- package/src/skill-set/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/src/skill-set/ask-matt/SKILL.md +91 -0
- package/src/skill-set/ask-matt/agents/openai.yaml +4 -0
- package/src/skill-set/code-review/LICENSE +21 -0
- package/src/skill-set/code-review/SKILL.md +89 -0
- package/src/skill-set/code-review/agents/openai.yaml +3 -0
- package/src/skill-set/codebase-design/DEEPENING.md +37 -0
- package/src/skill-set/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/src/skill-set/codebase-design/LICENSE +21 -0
- package/src/skill-set/codebase-design/SKILL.md +116 -0
- package/src/skill-set/codebase-design/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/LICENSE +21 -0
- package/src/skill-set/diagnosing-bugs/SKILL.md +140 -0
- package/src/skill-set/diagnosing-bugs/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/src/skill-set/domain-modeling/ADR-FORMAT.md +47 -0
- package/src/skill-set/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/src/skill-set/domain-modeling/LICENSE +21 -0
- package/src/skill-set/domain-modeling/SKILL.md +76 -0
- package/src/skill-set/domain-modeling/agents/openai.yaml +3 -0
- package/src/skill-set/grill-me/LICENSE +21 -0
- package/src/skill-set/grill-me/SKILL.md +8 -0
- package/src/skill-set/grill-me/agents/openai.yaml +4 -0
- package/src/skill-set/grill-with-docs/LICENSE +21 -0
- package/src/skill-set/grill-with-docs/SKILL.md +8 -0
- package/src/skill-set/grill-with-docs/agents/openai.yaml +4 -0
- package/src/skill-set/grilling/LICENSE +21 -0
- package/src/{starter-set → skill-set}/grilling/SKILL.md +1 -1
- package/src/skill-set/handoff/LICENSE +21 -0
- package/src/skill-set/handoff/SKILL.md +17 -0
- package/src/skill-set/handoff/agents/openai.yaml +4 -0
- package/src/skill-set/implement/LICENSE +21 -0
- package/src/skill-set/implement/SKILL.md +16 -0
- package/src/skill-set/implement/agents/openai.yaml +4 -0
- package/src/skill-set/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/src/skill-set/improve-codebase-architecture/LICENSE +21 -0
- package/src/skill-set/improve-codebase-architecture/SKILL.md +72 -0
- package/src/skill-set/improve-codebase-architecture/agents/openai.yaml +4 -0
- package/src/skill-set/prototype/LICENSE +21 -0
- package/src/skill-set/prototype/LOGIC.md +67 -0
- package/src/skill-set/prototype/SKILL.md +28 -0
- package/src/skill-set/prototype/UI.md +112 -0
- package/src/skill-set/prototype/agents/openai.yaml +3 -0
- package/src/skill-set/research/LICENSE +21 -0
- package/src/skill-set/research/SKILL.md +14 -0
- package/src/skill-set/research/agents/openai.yaml +3 -0
- package/src/skill-set/resolving-merge-conflicts/LICENSE +21 -0
- package/src/skill-set/resolving-merge-conflicts/SKILL.md +16 -0
- package/src/skill-set/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/LICENSE +21 -0
- package/src/skill-set/tdd/SKILL.md +40 -0
- package/src/skill-set/tdd/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/mocking.md +59 -0
- package/src/skill-set/tdd/tests.md +77 -0
- package/src/skill-set/teach/GLOSSARY-FORMAT.md +35 -0
- package/src/skill-set/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/src/skill-set/teach/LICENSE +21 -0
- package/src/skill-set/teach/MISSION-FORMAT.md +31 -0
- package/src/skill-set/teach/RESOURCES-FORMAT.md +32 -0
- package/src/skill-set/teach/SKILL.md +141 -0
- package/src/skill-set/teach/agents/openai.yaml +4 -0
- package/src/skill-set/to-questionnaire/LICENSE +21 -0
- package/src/skill-set/to-questionnaire/SKILL.md +55 -0
- package/src/skill-set/to-questionnaire/agents/openai.yaml +4 -0
- package/src/skill-set/to-spec/LICENSE +21 -0
- package/src/skill-set/to-spec/SKILL.md +76 -0
- package/src/skill-set/to-spec/agents/openai.yaml +4 -0
- package/src/skill-set/to-tickets/LICENSE +21 -0
- package/src/skill-set/to-tickets/SKILL.md +106 -0
- package/src/skill-set/to-tickets/agents/openai.yaml +4 -0
- package/src/skill-set/wait-what/LICENSE +21 -0
- package/src/skill-set/wait-what/SKILL.md +8 -0
- package/src/skill-set/wait-what/agents/openai.yaml +4 -0
- package/src/skill-set/wayfinder/LICENSE +21 -0
- package/src/skill-set/wayfinder/SKILL.md +129 -0
- package/src/skill-set/wayfinder/agents/openai.yaml +4 -0
- package/src/skill-set/writing-for-agents/LICENSE +21 -0
- package/src/skill-set/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/src/skill-set/writing-for-agents/SKILL.md +83 -0
- package/src/skill-set/writing-for-agents/agents/openai.yaml +3 -0
- package/src/skill-set.js +85 -0
- package/src/source.js +93 -54
- package/src/state.js +19 -1
- package/src/sync-library.js +7 -11
- package/src/sync-state.js +16 -3
- package/src/sync.js +129 -41
- package/src/tasks.js +75 -0
- package/src/telegram-view.js +214 -0
- package/src/telegram.js +161 -66
- package/src/turn-frames.js +69 -0
- package/src/worker.js +685 -184
- package/src/working-copy.js +1221 -0
- package/src/workspace.js +40 -32
- package/src/google/declared.js +0 -146
- package/src/google/id.js +0 -1
- package/src/instance/google.js +0 -1
- package/src/starter-set.js +0 -17
- /package/src/{starter-set/grilling → skill-set/ask-matt}/LICENSE +0 -0
- /package/src/{starter-set → skill-set}/grilling/agents/openai.yaml +0 -0
package/src/git-main.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import { constants as system } from "node:os";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import {
|
|
7
|
+
BRIDGE_NONCE_ENVIRONMENT,
|
|
8
|
+
BRIDGE_SOCKET_ENVIRONMENT,
|
|
9
|
+
WORKING_COPY_INDEX_METHOD,
|
|
10
|
+
WORKING_COPY_RECEIVED_REF,
|
|
11
|
+
} from "./contract.js";
|
|
12
|
+
import { bridgeCall } from "./mcp.js";
|
|
13
|
+
import { formatCommandError, KitRefusal } from "./refusal.js";
|
|
14
|
+
|
|
15
|
+
const SHIM = realpathSync(fileURLToPath(new URL("session-bin/git", import.meta.url)));
|
|
16
|
+
const VALUED = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--config-env", "--attr-source", "--super-prefix"]);
|
|
17
|
+
const RELAYED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
18
|
+
|
|
19
|
+
function nativeGit() {
|
|
20
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
21
|
+
if (directory === "") continue;
|
|
22
|
+
const candidate = join(directory, "git");
|
|
23
|
+
try {
|
|
24
|
+
if (!statSync(candidate).isFile() || realpathSync(candidate) === SHIM) continue;
|
|
25
|
+
accessSync(candidate, constants.X_OK);
|
|
26
|
+
return candidate;
|
|
27
|
+
} catch {}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function command(args) {
|
|
33
|
+
let index = 0;
|
|
34
|
+
while (index < args.length && args[index].startsWith("-")) index += VALUED.has(args[index]) ? 2 : 1;
|
|
35
|
+
return { globals: args.slice(0, index), name: args[index], rest: args.slice(index + 1) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function delegated(git, args) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const child = spawn(git, args, { stdio: "inherit" });
|
|
41
|
+
const relay = (signal) => child.kill(signal);
|
|
42
|
+
for (const signal of RELAYED) process.on(signal, relay);
|
|
43
|
+
const settle = (outcome) => {
|
|
44
|
+
for (const signal of RELAYED) process.off(signal, relay);
|
|
45
|
+
resolve(outcome);
|
|
46
|
+
};
|
|
47
|
+
child.once("error", (error) => {
|
|
48
|
+
process.stderr.write(`kit_git_unavailable: ${error.message}\n`);
|
|
49
|
+
settle({ status: 127, signal: null });
|
|
50
|
+
});
|
|
51
|
+
child.once("close", (status, signal) => settle({ status, signal }));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function captured(git, args, env = process.env) {
|
|
56
|
+
const run = spawnSync(git, args, { stdio: ["ignore", "pipe", "ignore"], maxBuffer: Infinity, env });
|
|
57
|
+
return run.status === 0 ? run.stdout.toString("utf8") : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function indexed(listing) {
|
|
61
|
+
const entries = new Map();
|
|
62
|
+
for (const record of listing.split("\0")) {
|
|
63
|
+
const tab = record.indexOf("\t");
|
|
64
|
+
if (tab < 0) continue;
|
|
65
|
+
const [mode, object, stage] = record.slice(0, tab).split(" ");
|
|
66
|
+
entries.set(record.slice(tab + 1), stage === "0" ? `${mode} ${object}` : null);
|
|
67
|
+
}
|
|
68
|
+
return entries;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renamings(output) {
|
|
72
|
+
return (output ?? "").split("\n").filter((line) => line.startsWith("Renaming ")).map((line) => line.slice(9));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function movedPaths(renaming, before, after) {
|
|
76
|
+
for (let at = renaming.indexOf(" to "); at >= 0; at = renaming.indexOf(" to ", at + 1)) {
|
|
77
|
+
const from = renaming.slice(0, at);
|
|
78
|
+
const to = renaming.slice(at + 4);
|
|
79
|
+
if (before.has(from) && !after.has(from) && after.get(to) === before.get(from)) return [{ from, to }];
|
|
80
|
+
}
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function recorded(cwd, change) {
|
|
85
|
+
const socket = process.env[BRIDGE_SOCKET_ENVIRONMENT];
|
|
86
|
+
const nonce = process.env[BRIDGE_NONCE_ENVIRONMENT];
|
|
87
|
+
if (socket === undefined || nonce === undefined) return;
|
|
88
|
+
try {
|
|
89
|
+
const frame = await bridgeCall({ socket, nonce }, { method: WORKING_COPY_INDEX_METHOD, params: { cwd, ...change } });
|
|
90
|
+
if (frame.error !== undefined) throw new KitRefusal(frame.error.message ?? "kit_house_refused");
|
|
91
|
+
} catch (refusal) {
|
|
92
|
+
process.stderr.write(`${formatCommandError(refusal)}: house git push will not see this index change\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function observed(git, { globals, name, rest }, args) {
|
|
97
|
+
const located = captured(git, [...globals, "rev-parse", "--show-toplevel", "--show-cdup", WORKING_COPY_RECEIVED_REF, "HEAD"]);
|
|
98
|
+
if (located === null) return delegated(git, args);
|
|
99
|
+
const [top, cdup, , head] = located.split("\n");
|
|
100
|
+
const listing = [...globals, "ls-files", "--stage", "-z", "--full-name", "--", cdup === "" ? "." : cdup];
|
|
101
|
+
const before = captured(git, listing);
|
|
102
|
+
const rehearsal = [...globals, "mv", "--dry-run", ...rest];
|
|
103
|
+
const planned = name === "mv" ? renamings(captured(git, rehearsal, { ...process.env, LC_ALL: "C" })) : [];
|
|
104
|
+
const outcome = await delegated(git, args);
|
|
105
|
+
const after = before === null ? null : captured(git, listing);
|
|
106
|
+
if (after === null) return outcome;
|
|
107
|
+
const [prior, next] = [indexed(before), indexed(after)];
|
|
108
|
+
const moves = outcome.status === 0 ? planned.flatMap((renaming) => movedPaths(renaming, prior, next)) : [];
|
|
109
|
+
const sources = new Set(moves.map((move) => move.from));
|
|
110
|
+
const destinations = new Set(moves.map((move) => move.to));
|
|
111
|
+
const gone = [...prior.keys()].filter((path) => !next.has(path) && !sources.has(path));
|
|
112
|
+
const arrived = [...next.keys()].filter((path) => !prior.has(path) && !destinations.has(path));
|
|
113
|
+
const heads = captured(git, [...globals, "rev-parse", "HEAD", ...(name === "rebase" ? ["ORIG_HEAD"] : [])]);
|
|
114
|
+
const [current, tip = head] = heads?.trim().split("\n") ?? [];
|
|
115
|
+
const commit = current === undefined || current === head ? [] : [tip, current];
|
|
116
|
+
if (moves.length > 0 || gone.length > 0 || arrived.length > 0 || commit.length > 0) {
|
|
117
|
+
await recorded(top, { moves, gone, arrived, commit, command: name });
|
|
118
|
+
}
|
|
119
|
+
return outcome;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function run(args) {
|
|
123
|
+
const git = nativeGit();
|
|
124
|
+
if (git === null) {
|
|
125
|
+
process.stderr.write("kit_git_unavailable\n");
|
|
126
|
+
return { status: 127, signal: null };
|
|
127
|
+
}
|
|
128
|
+
return observed(git, command(args), args);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
run(process.argv.slice(2)).then(({ status, signal }) => {
|
|
132
|
+
if (signal === null) {
|
|
133
|
+
process.exitCode = status;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
process.exitCode = 128 + system.signals[signal];
|
|
137
|
+
process.kill(process.pid, signal);
|
|
138
|
+
});
|
package/src/git.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { KitRefusal } from "./refusal.js";
|
|
3
|
+
|
|
4
|
+
function cancelledOr(signal, error, code) {
|
|
5
|
+
return signal?.aborted === true || error?.name === "AbortError"
|
|
6
|
+
? new KitRefusal("kit_command_cancelled")
|
|
7
|
+
: new KitRefusal(code);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function stdio(capture, input) {
|
|
11
|
+
if (input !== null) return ["pipe", capture ? "pipe" : "inherit", "inherit"];
|
|
12
|
+
return capture ? ["ignore", "pipe", "inherit"] : "inherit";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function runGit(cwd, args, options = {}) {
|
|
16
|
+
const { dependencies = {}, capture = false, input = null, signal, failure } = options;
|
|
17
|
+
if (signal?.aborted === true) return Promise.reject(new KitRefusal("kit_command_cancelled"));
|
|
18
|
+
const launcher = dependencies.spawn ?? spawn;
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
let child;
|
|
21
|
+
try {
|
|
22
|
+
child = launcher("git", args, {
|
|
23
|
+
cwd,
|
|
24
|
+
env: process.env,
|
|
25
|
+
stdio: stdio(capture, input),
|
|
26
|
+
signal,
|
|
27
|
+
killSignal: "SIGKILL",
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
reject(cancelledOr(signal, error, "kit_git_unavailable"));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const output = [];
|
|
34
|
+
let settled = false;
|
|
35
|
+
const finish = (operation) => {
|
|
36
|
+
if (settled) return;
|
|
37
|
+
settled = true;
|
|
38
|
+
operation();
|
|
39
|
+
};
|
|
40
|
+
child.stdout?.on("data", (chunk) => output.push(Buffer.from(chunk)));
|
|
41
|
+
child.once("error", (error) =>
|
|
42
|
+
finish(() => reject(cancelledOr(signal, error, "kit_git_unavailable"))),
|
|
43
|
+
);
|
|
44
|
+
child.once("close", (status) => {
|
|
45
|
+
finish(() => {
|
|
46
|
+
if (signal?.aborted === true) {
|
|
47
|
+
reject(new KitRefusal("kit_command_cancelled"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (status !== 0) {
|
|
51
|
+
reject(new KitRefusal(failure));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
resolve(Buffer.concat(output));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
if (input !== null) {
|
|
58
|
+
child.stdin.on("error", () => undefined);
|
|
59
|
+
child.stdin.end(input);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
package/src/google/calendar.js
CHANGED
|
@@ -4,9 +4,10 @@ import { googleJson } from "./gateway.js";
|
|
|
4
4
|
import { GOOGLE_SCOPES } from "./oauth.js";
|
|
5
5
|
|
|
6
6
|
const CALENDAR_ORIGIN = "https://www.googleapis.com";
|
|
7
|
+
const CALENDAR = "primary";
|
|
7
8
|
|
|
8
|
-
function cursorOf(value
|
|
9
|
-
if (value === null) return {
|
|
9
|
+
function cursorOf(value) {
|
|
10
|
+
if (value === null) return { pageToken: null, syncToken: null };
|
|
10
11
|
let cursor;
|
|
11
12
|
try {
|
|
12
13
|
cursor = JSON.parse(value);
|
|
@@ -16,16 +17,8 @@ function cursorOf(value, calendars) {
|
|
|
16
17
|
if (
|
|
17
18
|
typeof cursor !== "object" ||
|
|
18
19
|
cursor === null ||
|
|
19
|
-
!Number.isInteger(cursor.calendar) ||
|
|
20
|
-
cursor.calendar < 0 ||
|
|
21
|
-
cursor.calendar >= calendars.length ||
|
|
22
20
|
(cursor.pageToken !== null && typeof cursor.pageToken !== "string") ||
|
|
23
|
-
typeof cursor.
|
|
24
|
-
cursor.syncTokens === null ||
|
|
25
|
-
Array.isArray(cursor.syncTokens) ||
|
|
26
|
-
Object.entries(cursor.syncTokens).some(
|
|
27
|
-
([calendar, token]) => !calendars.includes(calendar) || typeof token !== "string",
|
|
28
|
-
)
|
|
21
|
+
(cursor.syncToken !== null && typeof cursor.syncToken !== "string")
|
|
29
22
|
) {
|
|
30
23
|
throw new KitRefusal("google_checkpoint_invalid", "calendar");
|
|
31
24
|
}
|
|
@@ -45,7 +38,7 @@ function authorOf(event) {
|
|
|
45
38
|
: event.organizer.email;
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
function normalizedEvent(event
|
|
41
|
+
function normalizedEvent(event) {
|
|
49
42
|
if (
|
|
50
43
|
typeof event?.id !== "string" ||
|
|
51
44
|
typeof event.etag !== "string" ||
|
|
@@ -71,45 +64,53 @@ function normalizedEvent(event, calendar) {
|
|
|
71
64
|
author: authorOf(event),
|
|
72
65
|
url: event.htmlLink ?? null,
|
|
73
66
|
attachments: [],
|
|
74
|
-
container: `google/calendar/${
|
|
67
|
+
container: `google/calendar/${CALENDAR}`,
|
|
75
68
|
};
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
function common(options) {
|
|
72
|
+
return {
|
|
73
|
+
directory: options.directory,
|
|
74
|
+
account: options.account,
|
|
75
|
+
requiredScopes: [GOOGLE_SCOPES.calendar],
|
|
76
|
+
signal: options.signal,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function pageUrl(cursor, limit, backfillFloor) {
|
|
81
|
+
const url = new URL(`/calendar/v3/calendars/${CALENDAR}/events`, CALENDAR_ORIGIN);
|
|
83
82
|
url.searchParams.set("maxResults", `${limit}`);
|
|
84
83
|
url.searchParams.set("singleEvents", "true");
|
|
85
84
|
url.searchParams.set("showDeleted", "true");
|
|
86
85
|
if (cursor.pageToken !== null) url.searchParams.set("pageToken", cursor.pageToken);
|
|
87
|
-
|
|
88
|
-
if (syncToken !== undefined) url.searchParams.set("syncToken", syncToken);
|
|
86
|
+
if (cursor.syncToken !== null) url.searchParams.set("syncToken", cursor.syncToken);
|
|
89
87
|
else if (backfillFloor !== null && backfillFloor !== undefined) {
|
|
90
88
|
url.searchParams.set("timeMin", backfillFloor);
|
|
91
89
|
}
|
|
92
90
|
return url.toString();
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
export async function calendarEmail(options, dependencies = {}) {
|
|
94
|
+
const calendar = await googleJson(
|
|
95
|
+
{ ...common(options), url: `${CALENDAR_ORIGIN}/calendar/v3/calendars/${CALENDAR}` },
|
|
96
|
+
dependencies,
|
|
97
|
+
);
|
|
98
|
+
if (typeof calendar.id !== "string") {
|
|
99
|
+
throw new KitRefusal("google_provider_response_invalid", "calendar");
|
|
100
|
+
}
|
|
101
|
+
return calendar.id;
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
export async function calendarPage(options, dependencies = {}) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const calendar = calendars[cursor.calendar];
|
|
99
|
-
const common = {
|
|
100
|
-
directory: options.directory,
|
|
101
|
-
instance: options.instance,
|
|
102
|
-
client: options.client,
|
|
103
|
-
requiredScopes: [GOOGLE_SCOPES.calendar],
|
|
104
|
-
signal: options.signal,
|
|
105
|
-
};
|
|
105
|
+
let cursor = cursorOf(options.cursor);
|
|
106
|
+
const shared = common(options);
|
|
106
107
|
let answer;
|
|
107
108
|
try {
|
|
108
109
|
answer = await googleJson(
|
|
109
110
|
{
|
|
110
|
-
...
|
|
111
|
+
...shared,
|
|
111
112
|
goneCode: "google_calendar_cursor_expired",
|
|
112
|
-
url: pageUrl(
|
|
113
|
+
url: pageUrl(cursor, options.limit, options.backfillFloor),
|
|
113
114
|
},
|
|
114
115
|
dependencies,
|
|
115
116
|
);
|
|
@@ -117,11 +118,9 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
117
118
|
if (!(error instanceof KitRefusal) || error.code !== "google_calendar_cursor_expired") {
|
|
118
119
|
throw error;
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
-
delete syncTokens[calendar];
|
|
122
|
-
cursor = { calendar: cursor.calendar, pageToken: null, syncTokens };
|
|
121
|
+
cursor = { pageToken: null, syncToken: null };
|
|
123
122
|
answer = await googleJson(
|
|
124
|
-
{ ...
|
|
123
|
+
{ ...shared, url: pageUrl(cursor, options.limit, options.backfillFloor) },
|
|
125
124
|
dependencies,
|
|
126
125
|
);
|
|
127
126
|
}
|
|
@@ -135,23 +134,18 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
135
134
|
}
|
|
136
135
|
let next;
|
|
137
136
|
if (answer.nextPageToken !== undefined) {
|
|
138
|
-
next = {
|
|
137
|
+
next = { pageToken: answer.nextPageToken, syncToken: cursor.syncToken };
|
|
139
138
|
} else {
|
|
140
139
|
if (typeof answer.nextSyncToken !== "string") {
|
|
141
140
|
throw new KitRefusal("google_provider_response_invalid", "calendar sync token");
|
|
142
141
|
}
|
|
143
|
-
|
|
144
|
-
next = {
|
|
145
|
-
calendar: cursor.calendar + 1 === calendars.length ? 0 : cursor.calendar + 1,
|
|
146
|
-
pageToken: null,
|
|
147
|
-
syncTokens,
|
|
148
|
-
};
|
|
142
|
+
next = { pageToken: null, syncToken: answer.nextSyncToken };
|
|
149
143
|
}
|
|
150
144
|
const normalized = [];
|
|
151
145
|
const dropped = [];
|
|
152
146
|
for (const event of items) {
|
|
153
147
|
try {
|
|
154
|
-
normalized.push(normalizedEvent(event
|
|
148
|
+
normalized.push(normalizedEvent(event));
|
|
155
149
|
} catch (error) {
|
|
156
150
|
if (
|
|
157
151
|
error instanceof KitRefusal &&
|
|
@@ -172,6 +166,6 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
172
166
|
items: normalized,
|
|
173
167
|
dropped,
|
|
174
168
|
cursor: JSON.stringify(next),
|
|
175
|
-
more: answer.nextPageToken !== undefined
|
|
169
|
+
more: answer.nextPageToken !== undefined,
|
|
176
170
|
};
|
|
177
171
|
}
|