very-happy-cli 0.2.125 → 0.2.127
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/{AcpBackend-DOQGItcS.mjs → AcpBackend-B9_vB_mY.mjs} +2 -2
- package/dist/{AcpBackend-ssQpTLks.cjs → AcpBackend-Cbykm5Pg.cjs} +2 -2
- package/dist/{AcpSessionManager-DHLc-D50.cjs → AcpSessionManager-DGCsNKRd.cjs} +1 -1
- package/dist/{AcpSessionManager-D4WkNsRs.mjs → AcpSessionManager-HsxKXreC.mjs} +1 -1
- package/dist/{agentGuidance-Cg9ZG1pP.cjs → agentGuidance-B-CazXmT.cjs} +64 -33
- package/dist/{agentGuidance-QpYwykb9.mjs → agentGuidance-CTyzeLCQ.mjs} +62 -35
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-CZNIz3W0.mjs → config-DotNknpP.mjs} +4 -4
- package/dist/{config-BxHOGtEK.cjs → config-Dwt-15hE.cjs} +4 -4
- package/dist/{index-CrSifgJM.mjs → index-B1qzJIMw.mjs} +249 -28
- package/dist/{index-Jrk6ud3g.mjs → index-C2kblG8-.mjs} +13 -9
- package/dist/{index-D-BgvQi5.mjs → index-CWPgxeLD.mjs} +4 -4
- package/dist/{index-Do-kjc30.cjs → index-DgjzHewW.cjs} +254 -31
- package/dist/{index-D_Rm9dw9.cjs → index-DhCEmP3d.cjs} +4 -4
- package/dist/{index-CBj99SO6.cjs → index-DyvVjr8Y.cjs} +13 -9
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-CPsdeOM4.cjs → installTerminalHooks-DfBvxRdw.cjs} +4 -4
- package/dist/{installTerminalHooks-Z1Jb6yjb.mjs → installTerminalHooks-cpuDExTT.mjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +1 -0
- package/dist/lib.d.mts +1 -0
- package/dist/lib.mjs +2 -2
- package/dist/{limits-DViGHwSj.cjs → limits-BDG3QjIB.cjs} +7 -7
- package/dist/{limits-FjbPJPsF.mjs → limits-D2XM3dvN.mjs} +9 -9
- package/dist/{mcp-BbQ5FWcQ.cjs → mcp-BZ3iYnBc.cjs} +4 -4
- package/dist/{mcp-zqPJ94Bk.mjs → mcp-CpUrsnoC.mjs} +4 -4
- package/dist/{runGemini-BnHGYX-H.cjs → runGemini-BE_nghsj.cjs} +6 -6
- package/dist/{runGemini-Bu49s_bN.mjs → runGemini-BY7uA-Uk.mjs} +6 -6
- package/dist/{runOpenClaw-CznAYPCh.mjs → runOpenClaw-Be_U4DHZ.mjs} +5 -5
- package/dist/{runOpenClaw-3Ld1eoWV.cjs → runOpenClaw-Cqun9jar.cjs} +5 -5
- package/dist/{send-BevgS1mf.mjs → send-D2xX42zg.mjs} +4 -4
- package/dist/{send-DCzyBxLl.cjs → send-DbqRxLQW.cjs} +4 -4
- package/dist/{sessions-DSoaBHwA.cjs → sessions-CtrMbMBJ.cjs} +4 -4
- package/dist/{sessions-Ba2oRTmw.mjs → sessions-D06cLJ7f.mjs} +4 -4
- package/dist/{spawn-CqMLY6dE.cjs → spawn-Cu_kM761.cjs} +4 -4
- package/dist/{spawn-j9KBfj4e.mjs → spawn-DMx1JKXo.mjs} +4 -4
- package/dist/{teams-CmU3K9vB.cjs → teams-C2MfKWFe.cjs} +67 -7
- package/dist/{teams-C0ggJTJ-.mjs → teams-D7ubdfTY.mjs} +64 -4
- package/dist/todo-hKSIY23F.mjs +128 -0
- package/dist/todo-yvodzg_y.cjs +132 -0
- package/dist/{types-BGHnQXl5.cjs → types-BuxqyzYf.cjs} +329 -12
- package/dist/{types-DLDkfrTC.mjs → types-Df_vXvzD.mjs} +320 -6
- package/package.json +7 -7
- package/dist/resources-CVl7NdOB.cjs +0 -83
- package/dist/resources-D-mn7ptO.mjs +0 -80
|
@@ -2,14 +2,49 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { unlink, rmdir, mkdir, lstat, readFile, writeFile, rename } from 'node:fs/promises';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
-
import {
|
|
5
|
+
import { d as teamRequest, e as createTeamsClient } from './agentGuidance-CTyzeLCQ.mjs';
|
|
6
|
+
import { d as readCredentialsForConfiguredRelay } from './limits-D2XM3dvN.mjs';
|
|
6
7
|
import { join, resolve, dirname } from 'node:path';
|
|
7
|
-
import { T as TEAM_SKILL } from './
|
|
8
|
-
import './limits-FjbPJPsF.mjs';
|
|
8
|
+
import { T as TEAM_SKILL } from './index-B1qzJIMw.mjs';
|
|
9
9
|
import 'node:fs';
|
|
10
10
|
import 'tweetnacl';
|
|
11
11
|
import 'chalk';
|
|
12
12
|
import 'node:util';
|
|
13
|
+
import './types-Df_vXvzD.mjs';
|
|
14
|
+
import 'axios';
|
|
15
|
+
import 'node:events';
|
|
16
|
+
import 'socket.io-client';
|
|
17
|
+
import '@paralleldrive/cuid2';
|
|
18
|
+
import 'child_process';
|
|
19
|
+
import 'util';
|
|
20
|
+
import 'fs/promises';
|
|
21
|
+
import 'crypto';
|
|
22
|
+
import 'path';
|
|
23
|
+
import 'cross-spawn';
|
|
24
|
+
import 'url';
|
|
25
|
+
import 'os';
|
|
26
|
+
import 'node:child_process';
|
|
27
|
+
import 'node-pty';
|
|
28
|
+
import 'fs';
|
|
29
|
+
import 'node:readline';
|
|
30
|
+
import '@anthropic-ai/sandbox-runtime';
|
|
31
|
+
import 'expo-server-sdk';
|
|
32
|
+
import 'ink';
|
|
33
|
+
import 'react';
|
|
34
|
+
import '@anthropic-ai/claude-agent-sdk';
|
|
35
|
+
import 'ps-list';
|
|
36
|
+
import 'tmp';
|
|
37
|
+
import 'open';
|
|
38
|
+
import 'fastify';
|
|
39
|
+
import 'fastify-type-provider-zod';
|
|
40
|
+
import 'node:module';
|
|
41
|
+
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
42
|
+
import 'node:http';
|
|
43
|
+
import '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
44
|
+
import 'http';
|
|
45
|
+
import 'inquirer';
|
|
46
|
+
import 'node:url';
|
|
47
|
+
import 'node:readline/promises';
|
|
13
48
|
|
|
14
49
|
const legacyTask = z.object({
|
|
15
50
|
id: z.string().regex(/^T-\d{3,}$/),
|
|
@@ -43,6 +78,25 @@ function previewLegacyTeamsMigration(input) {
|
|
|
43
78
|
};
|
|
44
79
|
}
|
|
45
80
|
|
|
81
|
+
async function setTeamPermissionMode(teamId, mode, requestId) {
|
|
82
|
+
if (process.env.VH_TEAM_SCOPE_FILE || process.env.HAPPY_SESSION_ID) {
|
|
83
|
+
throw new Error("Change team execution mode from the owner Web UI or an independent CLI terminal");
|
|
84
|
+
}
|
|
85
|
+
if (!teamId?.trim() || !requestId?.trim()) throw new Error("Team and request ID are required");
|
|
86
|
+
if (mode !== "default" && mode !== "bypassPermissions") throw new Error("Mode must be default or bypassPermissions");
|
|
87
|
+
const credentials = await readCredentialsForConfiguredRelay();
|
|
88
|
+
if (!credentials) throw new Error("Sign in before changing team execution mode");
|
|
89
|
+
const result = await teamRequest(
|
|
90
|
+
`/v1/teams/${encodeURIComponent(teamId)}/actions`,
|
|
91
|
+
"POST",
|
|
92
|
+
credentials.token,
|
|
93
|
+
false,
|
|
94
|
+
{ requestId, action: { type: "set-permission-mode", permissionMode: mode } }
|
|
95
|
+
);
|
|
96
|
+
const { credential: _credential, ...safe } = result;
|
|
97
|
+
return safe;
|
|
98
|
+
}
|
|
99
|
+
|
|
46
100
|
async function assertPrivateDestination(directory) {
|
|
47
101
|
for (let path = directory; ; path = dirname(path)) {
|
|
48
102
|
try {
|
|
@@ -113,7 +167,7 @@ async function handleTeamsCommand(args) {
|
|
|
113
167
|
return index < 0 ? void 0 : args[index + 1];
|
|
114
168
|
};
|
|
115
169
|
if (!command || command === "--help") {
|
|
116
|
-
console.log("very-happy teams install|uninstall --host claude|codex|pi [--home PATH] [--apply]\nvery-happy teams doctor --host pi\nvery-happy teams migration-preview --file LEDGER_JSON\nvery-happy teams inspect\nvery-happy teams create|join --name NAME [--machine-id ID | --team-id ID] [--session-id ID] [--request-id ID]\nvery-happy teams action --json ACTION_JSON --request-id ID\nInstallation previews changes unless --apply is supplied. Managed sessions receive tools automatically.");
|
|
170
|
+
console.log("very-happy teams install|uninstall --host claude|codex|pi [--home PATH] [--apply]\nvery-happy teams doctor --host pi\nvery-happy teams migration-preview --file LEDGER_JSON\nvery-happy teams inspect\nvery-happy teams permissions --team-id ID --mode default|bypassPermissions --request-id ID\nvery-happy teams create|join --name NAME [--machine-id ID | --team-id ID] [--session-id ID] [--request-id ID]\nvery-happy teams action --json ACTION_JSON --request-id ID\nInstallation previews changes unless --apply is supplied. Managed sessions receive tools automatically.");
|
|
117
171
|
return;
|
|
118
172
|
}
|
|
119
173
|
if (command === "install" || command === "uninstall") {
|
|
@@ -142,6 +196,12 @@ async function handleTeamsCommand(args) {
|
|
|
142
196
|
console.log(JSON.stringify(previewLegacyTeamsMigration(JSON.parse(await readFile(file, "utf8"))), null, 2));
|
|
143
197
|
return;
|
|
144
198
|
}
|
|
199
|
+
if (command === "permissions") {
|
|
200
|
+
const teamId = value("--team-id"), mode = value("--mode"), requestId = value("--request-id");
|
|
201
|
+
if (!teamId || !mode || !requestId) throw new Error("--team-id, --mode and --request-id are required");
|
|
202
|
+
console.log(JSON.stringify(await setTeamPermissionMode(teamId, mode, requestId)));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
145
205
|
const client = createTeamsClient(value("--session-id"));
|
|
146
206
|
let result;
|
|
147
207
|
if (command === "inspect") result = await client.inspect();
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { d as readCredentialsForConfiguredRelay, c as configuration } from './limits-D2XM3dvN.mjs';
|
|
2
|
+
import { b as createBuiltinTodoClient, B as BUILTIN_TODO_SKILL } from './types-Df_vXvzD.mjs';
|
|
3
|
+
import { c as readTeamScope, t as teamScopePath } from './agentGuidance-CTyzeLCQ.mjs';
|
|
4
|
+
import 'node:fs/promises';
|
|
5
|
+
import 'node:fs';
|
|
6
|
+
import 'node:os';
|
|
7
|
+
import 'node:path';
|
|
8
|
+
import 'zod';
|
|
9
|
+
import 'node:crypto';
|
|
10
|
+
import 'tweetnacl';
|
|
11
|
+
import 'chalk';
|
|
12
|
+
import 'node:util';
|
|
13
|
+
import 'axios';
|
|
14
|
+
import 'node:events';
|
|
15
|
+
import 'socket.io-client';
|
|
16
|
+
import '@paralleldrive/cuid2';
|
|
17
|
+
import 'child_process';
|
|
18
|
+
import 'util';
|
|
19
|
+
import 'fs/promises';
|
|
20
|
+
import 'crypto';
|
|
21
|
+
import 'path';
|
|
22
|
+
import 'cross-spawn';
|
|
23
|
+
import 'url';
|
|
24
|
+
import 'os';
|
|
25
|
+
import 'node:child_process';
|
|
26
|
+
import 'node-pty';
|
|
27
|
+
import 'fs';
|
|
28
|
+
import 'node:readline';
|
|
29
|
+
import '@anthropic-ai/sandbox-runtime';
|
|
30
|
+
import 'expo-server-sdk';
|
|
31
|
+
|
|
32
|
+
async function authenticatedBuiltinTodoClient() {
|
|
33
|
+
if (process.env.VH_TEAM_SCOPE_FILE !== void 0) {
|
|
34
|
+
throw new Error("Scoped Teams sessions cannot access account Todos; ask the owner session.");
|
|
35
|
+
}
|
|
36
|
+
const sessionId = process.env.HAPPY_SESSION_ID;
|
|
37
|
+
if (sessionId && await readTeamScope(teamScopePath(sessionId))) {
|
|
38
|
+
throw new Error("Scoped Teams sessions cannot access account Todos; ask the owner session.");
|
|
39
|
+
}
|
|
40
|
+
const credentials = await readCredentialsForConfiguredRelay();
|
|
41
|
+
if (!credentials) throw new Error("Sign in to Very Happy with `very-happy auth login` before accessing Todos.");
|
|
42
|
+
return createBuiltinTodoClient(credentials, { serverUrl: configuration.serverUrl, clientId: "very-happy-cli-todo" });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const TODO_HELP = `very-happy todo \u2014 account-owned built-in Todos (JSON output)
|
|
46
|
+
very-happy todo list
|
|
47
|
+
very-happy todo get ID
|
|
48
|
+
very-happy todo add --id UUID --title TEXT [--note TEXT]
|
|
49
|
+
very-happy todo edit ID --version N [--title TEXT] [--note TEXT]
|
|
50
|
+
very-happy todo complete|reopen|delete ID --version N
|
|
51
|
+
very-happy todo skill
|
|
52
|
+
|
|
53
|
+
Use the version returned by list/get. Conflicts require reading the latest task
|
|
54
|
+
before deciding what to change. Reuse the same add UUID and arguments after an
|
|
55
|
+
unknown result. Use --title=TEXT or --note=TEXT for values starting with --.
|
|
56
|
+
These commands use the current relay and login; they do not
|
|
57
|
+
operate external providers or agent execution plans. skill prints the official
|
|
58
|
+
instructions without authentication. Pass --help for this reference.`;
|
|
59
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
60
|
+
function parseTodoArgs(args) {
|
|
61
|
+
if (args.length === 0 || args.length === 1 && args[0] === "--help") return { action: "help" };
|
|
62
|
+
const action = args[0];
|
|
63
|
+
if (!["list", "get", "add", "edit", "complete", "reopen", "delete", "skill"].includes(action)) throw new Error("Unknown Todo command; use todo --help");
|
|
64
|
+
if (args.length === 2 && args[1] === "--help") return { action: "help" };
|
|
65
|
+
const flags = {};
|
|
66
|
+
let id;
|
|
67
|
+
for (let i = 1; i < args.length; i++) {
|
|
68
|
+
const arg = args[i];
|
|
69
|
+
if (arg.startsWith("--")) {
|
|
70
|
+
const separator = arg.indexOf("=");
|
|
71
|
+
const flag = separator === -1 ? arg : arg.slice(0, separator);
|
|
72
|
+
if (!["--id", "--version", "--title", "--note"].includes(flag) || separator !== -1 && flag !== "--title" && flag !== "--note") throw new Error(`Unknown Todo option: ${arg}`);
|
|
73
|
+
if (Object.hasOwn(flags, flag)) throw new Error(`Duplicate Todo option: ${flag}`);
|
|
74
|
+
const value = separator === -1 ? args[++i] : arg.slice(separator + 1);
|
|
75
|
+
if (value === void 0 || separator === -1 && value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
|
|
76
|
+
flags[flag] = value;
|
|
77
|
+
} else {
|
|
78
|
+
if (id !== void 0) throw new Error("Unexpected Todo argument");
|
|
79
|
+
id = arg;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const allowed = action === "add" ? ["--id", "--title", "--note"] : action === "edit" ? ["--version", "--title", "--note"] : ["complete", "reopen", "delete"].includes(action) ? ["--version"] : [];
|
|
83
|
+
for (const flag of Object.keys(flags)) if (!allowed.includes(flag)) throw new Error(`${flag} is not supported for ${action}`);
|
|
84
|
+
const needsId = ["get", "edit", "complete", "reopen", "delete"].includes(action);
|
|
85
|
+
if (needsId ? !id || !UUID.test(id) : id !== void 0) throw new Error(needsId ? "A valid Todo UUID is required" : "Unexpected Todo ID");
|
|
86
|
+
if (action === "add" && (!flags["--id"] || !UUID.test(flags["--id"]))) throw new Error("add requires --id UUID; reuse it when retrying");
|
|
87
|
+
if (action === "add" && flags["--title"] === void 0) throw new Error("add requires --title");
|
|
88
|
+
if (action === "edit" && flags["--title"] === void 0 && flags["--note"] === void 0) throw new Error("edit requires --title or --note");
|
|
89
|
+
if (flags["--title"] !== void 0 && (!flags["--title"].trim() || flags["--title"].trim().length > 500)) throw new Error("Todo title must contain 1\u2013500 characters");
|
|
90
|
+
if (flags["--note"] !== void 0 && flags["--note"].length > 8e3) throw new Error("Todo note exceeds 8000 characters");
|
|
91
|
+
let version;
|
|
92
|
+
if (["edit", "complete", "reopen", "delete"].includes(action)) {
|
|
93
|
+
const value = flags["--version"];
|
|
94
|
+
if (value === void 0 || !/^(0|[1-9][0-9]*)$/.test(value) || !Number.isSafeInteger(Number(value))) throw new Error("A non-negative safe integer --version is required");
|
|
95
|
+
version = Number(value);
|
|
96
|
+
}
|
|
97
|
+
return { action, id: action === "add" ? flags["--id"] : id, version, title: flags["--title"], note: flags["--note"] };
|
|
98
|
+
}
|
|
99
|
+
async function handleTodoCommand(args) {
|
|
100
|
+
const command = parseTodoArgs(args);
|
|
101
|
+
if (command.action === "help") {
|
|
102
|
+
console.log(TODO_HELP);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (command.action === "skill") {
|
|
106
|
+
console.log(BUILTIN_TODO_SKILL);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const client = await authenticatedBuiltinTodoClient();
|
|
110
|
+
let result;
|
|
111
|
+
if (command.action === "list") result = await client.list();
|
|
112
|
+
else if (command.action === "add") result = await client.create(command.title, command.note ?? "", command.id);
|
|
113
|
+
else {
|
|
114
|
+
const record = await client.get(command.id);
|
|
115
|
+
if (command.action === "get") result = record;
|
|
116
|
+
else {
|
|
117
|
+
if (!record) throw new Error("Todo not found; it may have been deleted.");
|
|
118
|
+
if (record.version !== command.version) throw new Error("Todo version conflict; read the latest task before deciding what to change.");
|
|
119
|
+
if (command.action === "delete") {
|
|
120
|
+
await client.remove(record);
|
|
121
|
+
result = { id: record.id, deleted: true };
|
|
122
|
+
} else result = await client.update(record, command.action === "edit" ? { ...command.title !== void 0 ? { title: command.title } : {}, ...command.note !== void 0 ? { note: command.note } : {} } : { status: command.action === "complete" ? "done" : "open" });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
console.log(JSON.stringify({ serverUrl: configuration.serverUrl, result }));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { TODO_HELP, handleTodoCommand, parseTodoArgs };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var persistence = require('./limits-BDG3QjIB.cjs');
|
|
4
|
+
var api = require('./types-BuxqyzYf.cjs');
|
|
5
|
+
var agentGuidance = require('./agentGuidance-B-CazXmT.cjs');
|
|
6
|
+
require('node:fs/promises');
|
|
7
|
+
require('node:fs');
|
|
8
|
+
require('node:os');
|
|
9
|
+
require('node:path');
|
|
10
|
+
require('zod');
|
|
11
|
+
require('node:crypto');
|
|
12
|
+
require('tweetnacl');
|
|
13
|
+
require('chalk');
|
|
14
|
+
require('node:util');
|
|
15
|
+
require('axios');
|
|
16
|
+
require('node:events');
|
|
17
|
+
require('socket.io-client');
|
|
18
|
+
require('@paralleldrive/cuid2');
|
|
19
|
+
require('child_process');
|
|
20
|
+
require('util');
|
|
21
|
+
require('fs/promises');
|
|
22
|
+
require('crypto');
|
|
23
|
+
require('path');
|
|
24
|
+
require('cross-spawn');
|
|
25
|
+
require('url');
|
|
26
|
+
require('os');
|
|
27
|
+
require('node:child_process');
|
|
28
|
+
require('node-pty');
|
|
29
|
+
require('fs');
|
|
30
|
+
require('node:readline');
|
|
31
|
+
require('@anthropic-ai/sandbox-runtime');
|
|
32
|
+
require('expo-server-sdk');
|
|
33
|
+
|
|
34
|
+
async function authenticatedBuiltinTodoClient() {
|
|
35
|
+
if (process.env.VH_TEAM_SCOPE_FILE !== void 0) {
|
|
36
|
+
throw new Error("Scoped Teams sessions cannot access account Todos; ask the owner session.");
|
|
37
|
+
}
|
|
38
|
+
const sessionId = process.env.HAPPY_SESSION_ID;
|
|
39
|
+
if (sessionId && await agentGuidance.readTeamScope(agentGuidance.teamScopePath(sessionId))) {
|
|
40
|
+
throw new Error("Scoped Teams sessions cannot access account Todos; ask the owner session.");
|
|
41
|
+
}
|
|
42
|
+
const credentials = await persistence.readCredentialsForConfiguredRelay();
|
|
43
|
+
if (!credentials) throw new Error("Sign in to Very Happy with `very-happy auth login` before accessing Todos.");
|
|
44
|
+
return api.createBuiltinTodoClient(credentials, { serverUrl: persistence.configuration.serverUrl, clientId: "very-happy-cli-todo" });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const TODO_HELP = `very-happy todo \u2014 account-owned built-in Todos (JSON output)
|
|
48
|
+
very-happy todo list
|
|
49
|
+
very-happy todo get ID
|
|
50
|
+
very-happy todo add --id UUID --title TEXT [--note TEXT]
|
|
51
|
+
very-happy todo edit ID --version N [--title TEXT] [--note TEXT]
|
|
52
|
+
very-happy todo complete|reopen|delete ID --version N
|
|
53
|
+
very-happy todo skill
|
|
54
|
+
|
|
55
|
+
Use the version returned by list/get. Conflicts require reading the latest task
|
|
56
|
+
before deciding what to change. Reuse the same add UUID and arguments after an
|
|
57
|
+
unknown result. Use --title=TEXT or --note=TEXT for values starting with --.
|
|
58
|
+
These commands use the current relay and login; they do not
|
|
59
|
+
operate external providers or agent execution plans. skill prints the official
|
|
60
|
+
instructions without authentication. Pass --help for this reference.`;
|
|
61
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
62
|
+
function parseTodoArgs(args) {
|
|
63
|
+
if (args.length === 0 || args.length === 1 && args[0] === "--help") return { action: "help" };
|
|
64
|
+
const action = args[0];
|
|
65
|
+
if (!["list", "get", "add", "edit", "complete", "reopen", "delete", "skill"].includes(action)) throw new Error("Unknown Todo command; use todo --help");
|
|
66
|
+
if (args.length === 2 && args[1] === "--help") return { action: "help" };
|
|
67
|
+
const flags = {};
|
|
68
|
+
let id;
|
|
69
|
+
for (let i = 1; i < args.length; i++) {
|
|
70
|
+
const arg = args[i];
|
|
71
|
+
if (arg.startsWith("--")) {
|
|
72
|
+
const separator = arg.indexOf("=");
|
|
73
|
+
const flag = separator === -1 ? arg : arg.slice(0, separator);
|
|
74
|
+
if (!["--id", "--version", "--title", "--note"].includes(flag) || separator !== -1 && flag !== "--title" && flag !== "--note") throw new Error(`Unknown Todo option: ${arg}`);
|
|
75
|
+
if (Object.hasOwn(flags, flag)) throw new Error(`Duplicate Todo option: ${flag}`);
|
|
76
|
+
const value = separator === -1 ? args[++i] : arg.slice(separator + 1);
|
|
77
|
+
if (value === void 0 || separator === -1 && value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
|
|
78
|
+
flags[flag] = value;
|
|
79
|
+
} else {
|
|
80
|
+
if (id !== void 0) throw new Error("Unexpected Todo argument");
|
|
81
|
+
id = arg;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const allowed = action === "add" ? ["--id", "--title", "--note"] : action === "edit" ? ["--version", "--title", "--note"] : ["complete", "reopen", "delete"].includes(action) ? ["--version"] : [];
|
|
85
|
+
for (const flag of Object.keys(flags)) if (!allowed.includes(flag)) throw new Error(`${flag} is not supported for ${action}`);
|
|
86
|
+
const needsId = ["get", "edit", "complete", "reopen", "delete"].includes(action);
|
|
87
|
+
if (needsId ? !id || !UUID.test(id) : id !== void 0) throw new Error(needsId ? "A valid Todo UUID is required" : "Unexpected Todo ID");
|
|
88
|
+
if (action === "add" && (!flags["--id"] || !UUID.test(flags["--id"]))) throw new Error("add requires --id UUID; reuse it when retrying");
|
|
89
|
+
if (action === "add" && flags["--title"] === void 0) throw new Error("add requires --title");
|
|
90
|
+
if (action === "edit" && flags["--title"] === void 0 && flags["--note"] === void 0) throw new Error("edit requires --title or --note");
|
|
91
|
+
if (flags["--title"] !== void 0 && (!flags["--title"].trim() || flags["--title"].trim().length > 500)) throw new Error("Todo title must contain 1\u2013500 characters");
|
|
92
|
+
if (flags["--note"] !== void 0 && flags["--note"].length > 8e3) throw new Error("Todo note exceeds 8000 characters");
|
|
93
|
+
let version;
|
|
94
|
+
if (["edit", "complete", "reopen", "delete"].includes(action)) {
|
|
95
|
+
const value = flags["--version"];
|
|
96
|
+
if (value === void 0 || !/^(0|[1-9][0-9]*)$/.test(value) || !Number.isSafeInteger(Number(value))) throw new Error("A non-negative safe integer --version is required");
|
|
97
|
+
version = Number(value);
|
|
98
|
+
}
|
|
99
|
+
return { action, id: action === "add" ? flags["--id"] : id, version, title: flags["--title"], note: flags["--note"] };
|
|
100
|
+
}
|
|
101
|
+
async function handleTodoCommand(args) {
|
|
102
|
+
const command = parseTodoArgs(args);
|
|
103
|
+
if (command.action === "help") {
|
|
104
|
+
console.log(TODO_HELP);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (command.action === "skill") {
|
|
108
|
+
console.log(api.BUILTIN_TODO_SKILL);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const client = await authenticatedBuiltinTodoClient();
|
|
112
|
+
let result;
|
|
113
|
+
if (command.action === "list") result = await client.list();
|
|
114
|
+
else if (command.action === "add") result = await client.create(command.title, command.note ?? "", command.id);
|
|
115
|
+
else {
|
|
116
|
+
const record = await client.get(command.id);
|
|
117
|
+
if (command.action === "get") result = record;
|
|
118
|
+
else {
|
|
119
|
+
if (!record) throw new Error("Todo not found; it may have been deleted.");
|
|
120
|
+
if (record.version !== command.version) throw new Error("Todo version conflict; read the latest task before deciding what to change.");
|
|
121
|
+
if (command.action === "delete") {
|
|
122
|
+
await client.remove(record);
|
|
123
|
+
result = { id: record.id, deleted: true };
|
|
124
|
+
} else result = await client.update(record, command.action === "edit" ? { ...command.title !== void 0 ? { title: command.title } : {}, ...command.note !== void 0 ? { note: command.note } : {} } : { status: command.action === "complete" ? "done" : "open" });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
console.log(JSON.stringify({ serverUrl: persistence.configuration.serverUrl, result }));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
exports.TODO_HELP = TODO_HELP;
|
|
131
|
+
exports.handleTodoCommand = handleTodoCommand;
|
|
132
|
+
exports.parseTodoArgs = parseTodoArgs;
|