@assethub/cli 0.1.33 → 0.1.36
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 +67 -24
- package/dist/agentSetup.d.ts +35 -40
- package/dist/agentSetup.d.ts.map +1 -1
- package/dist/agentSetup.js +72 -113
- package/dist/hooks/command.d.ts +1 -0
- package/dist/hooks/command.d.ts.map +1 -1
- package/dist/hooks/command.js +2 -1
- package/dist/hooks/save.d.ts +14 -0
- package/dist/hooks/save.d.ts.map +1 -1
- package/dist/hooks/save.js +20 -0
- package/dist/hooks/types.d.ts +2 -0
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/hooks/upload.d.ts +6 -0
- package/dist/hooks/upload.d.ts.map +1 -1
- package/dist/hooks/upload.js +16 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -30
- package/dist/setupCommand.d.ts +53 -2
- package/dist/setupCommand.d.ts.map +1 -1
- package/dist/setupCommand.js +344 -131
- package/dist/updateNotice.d.ts +71 -0
- package/dist/updateNotice.d.ts.map +1 -0
- package/dist/updateNotice.js +189 -0
- package/package.json +1 -1
- package/skills/assethub/SKILL.md +20 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Command line interface for AssetHub.
|
|
|
4
4
|
|
|
5
5
|
## Quick setup
|
|
6
6
|
|
|
7
|
-
Connect Claude Code and
|
|
7
|
+
Connect Claude Code, Codex and Cursor to AssetHub in one step:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
assethub setup # prompts for your API key and workspace
|
|
@@ -12,25 +12,79 @@ printf '%s' "$KEY" | assethub setup --api-key-stdin --workspace <id> --yes
|
|
|
12
12
|
assethub setup --dry-run # show every command and file change, change nothing
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
`setup` runs these steps in order, and is safe to run again: each step checks the
|
|
16
|
+
current state first and reports it as unchanged, updated, or (with `--dry-run`)
|
|
17
|
+
what it would change.
|
|
18
|
+
|
|
19
|
+
| Step | What it does |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `login` | Reuses a saved key, or reads one from `--api-key-stdin`, `ASSETHUB_API_KEY` or a prompt. |
|
|
22
|
+
| `workspace` | Uses `--workspace`, the saved selection, or asks you to pick one. |
|
|
23
|
+
| agents | Finds Claude Code, Codex and Cursor on this machine (their folder or command), or uses `--agent`. |
|
|
24
|
+
| `mcp` | Registers the `assethub` MCP server in each agent: Claude Code through `claude mcp add-json ... --scope user` (Claude Code owns `~/.claude.json`; when `claude` is not on PATH, for example with only an IDE extension, setup writes the same entry into that file itself), Codex as an `[mcp_servers.assethub]` section in `$CODEX_HOME/config.toml`, Cursor as `mcpServers.assethub` in `~/.cursor/mcp.json`. A file setup writes itself is backed up as `<file>.bak-<timestamp>` first and replaced in one step. If `claude mcp add` fails while replacing an outdated entry, the previous entry is put back. |
|
|
25
|
+
| `skills` | Installs the bundled `assethub` skill under `~/.agents/skills/assethub` and links it into each agent's skill folder, so the agent knows the command sequence, cost rules and recovery steps. |
|
|
26
|
+
| `app-env` | macOS: makes `ASSETHUB_API_KEY` visible to apps started from the Dock (see below). |
|
|
27
|
+
| `doctor` | Runs `doctor --mcp` and prints a checklist (`--json` for machine output). |
|
|
28
|
+
|
|
29
|
+
Choose agents with `--agent claude-code|codex|cursor` (repeat it, or separate
|
|
30
|
+
names with commas; `claude` also works). Run only some steps with
|
|
31
|
+
`--only <step,...>` or leave some out with `--skip <step,...>`; `--no-skills` and
|
|
32
|
+
`--no-app-env` are short for skipping those. `--project` writes the MCP config
|
|
33
|
+
and the skill into the current folder instead of your home folder
|
|
34
|
+
(`.mcp.json`, `.codex/config.toml`, `.cursor/mcp.json`, `.agents/skills/assethub`
|
|
35
|
+
with relative links), for a repository to share:
|
|
22
36
|
|
|
23
|
-
|
|
37
|
+
```sh
|
|
38
|
+
assethub setup --only skills --project # put the agent guidance in this repository
|
|
39
|
+
assethub setup --only mcp,skills # wire the agents without logging in
|
|
40
|
+
assethub doctor --setup # report missing or outdated MCP config and skill
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`assethub doctor --setup` checks the same steps without changing anything and
|
|
44
|
+
fails when an agent's MCP entry or the skill is missing or out of date; run
|
|
45
|
+
`assethub setup` to fix it. Directories setup did not create are left untouched.
|
|
46
|
+
After a CLI upgrade, the next command refreshes the installed skill automatically.
|
|
47
|
+
Set `ASSETHUB_CLI_HOME` to redirect these writes, for example in tests.
|
|
48
|
+
|
|
49
|
+
`assethub init` is deprecated: it now runs `setup --only mcp,skills` and prints
|
|
50
|
+
the result as JSON, and will be removed in a later version. `--client
|
|
51
|
+
claude|codex|both` still works and means the same as `--agent`.
|
|
52
|
+
|
|
53
|
+
MCP does not need the CLI. Setup is only a shortcut: every agent calls the hosted
|
|
24
54
|
`/api/mcp` server directly and read the key from the `ASSETHUB_API_KEY`
|
|
25
55
|
environment variable. The Claude Code entry is
|
|
26
56
|
`claude mcp add-json assethub '{"type":"http","url":"https://app.assethub.io/api/mcp","headers":{"Authorization":"Bearer ${ASSETHUB_API_KEY}","X-AssetHub-Workspace":"<id>"}}' --scope user`,
|
|
27
57
|
so the key is never written to Claude Code's config or passed on a command line,
|
|
28
|
-
and removing the CLI does not disconnect MCP. Codex uses `bearer_token_env_var
|
|
58
|
+
and removing the CLI does not disconnect MCP. Codex uses `bearer_token_env_var`,
|
|
59
|
+
and Cursor `${env:ASSETHUB_API_KEY}`.
|
|
29
60
|
Setup never writes the key to a shell profile; run `assethub setup --print-env` to
|
|
30
61
|
print the `export ASSETHUB_API_KEY=...` line, and export it in the shell that
|
|
31
62
|
starts Claude Code or Codex. The key is redacted
|
|
32
63
|
(`ah_…last4`) everywhere else.
|
|
33
64
|
|
|
65
|
+
## Staying up to date
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
assethub update --check # JSON: up_to_date or update_available
|
|
69
|
+
assethub update # upgrade a global install now; the saved login is kept
|
|
70
|
+
assethub setup --no-auto-update # turn background updates off (--auto-update turns them on)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Auto-update is on by default. Once a day, a detached background process checks
|
|
74
|
+
for a newer release and installs it with the package manager that installed the
|
|
75
|
+
CLI, the same way `assethub update --yes` does, keeping the saved login. No
|
|
76
|
+
command waits for it, and an install never happens inside a running command; the
|
|
77
|
+
next command prints one line on stderr (`updated itself from 0.1.36 to 0.1.37`).
|
|
78
|
+
stdout stays one JSON object. If the install fails, the CLI keeps the current
|
|
79
|
+
version and says so, then keeps announcing the newer release.
|
|
80
|
+
|
|
81
|
+
It applies only to a global install (not `npx`, a project dependency or a source
|
|
82
|
+
checkout) and is off in CI, with `ASSETHUB_NO_AUTO_UPDATE=1` (no install, notice
|
|
83
|
+
only) or with `ASSETHUB_NO_UPDATE_CHECK=1` (no check at all). The choice is saved
|
|
84
|
+
in `~/.assethub/settings.json`, and `assethub setup` shows it as its `auto-update`
|
|
85
|
+
step. The bundled agent skill also asks agents to run `assethub update --check`
|
|
86
|
+
at the start of each session.
|
|
87
|
+
|
|
34
88
|
## API discovery and Workspace Skills
|
|
35
89
|
|
|
36
90
|
Use `api search [query]` to discover the authenticated server catalog and
|
|
@@ -146,8 +200,7 @@ authentication. Existing workspace keys and user-token login remain supported.
|
|
|
146
200
|
```sh
|
|
147
201
|
assethub --version
|
|
148
202
|
assethub doctor --mcp --profile my-workspace
|
|
149
|
-
assethub
|
|
150
|
-
assethub init
|
|
203
|
+
assethub doctor --setup
|
|
151
204
|
assethub mcp config --client cursor
|
|
152
205
|
assethub mcp config --client codex
|
|
153
206
|
```
|
|
@@ -168,18 +221,8 @@ Use the same key as your CLI profile. Personal profiles include the selected
|
|
|
168
221
|
For a different API origin, pass
|
|
169
222
|
`--base-url https://your-host/prefix` (HTTP is accepted only for localhost).
|
|
170
223
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
own configuration (merging with what is already there), installs the bundled
|
|
174
|
-
`assethub` skill under `~/.agents/skills/assethub`, and links it into each agent's
|
|
175
|
-
skill directory so the agent knows the correct command sequence, cost rules, and
|
|
176
|
-
recovery steps without being told. Pass `--agent <name>` to choose agents
|
|
177
|
-
explicitly, `--project` to write project-level MCP files into the current
|
|
178
|
-
directory instead of your home, and `--dry-run` to see the plan without writing.
|
|
179
|
-
`init` never stores a key: the MCP entries read `ASSETHUB_API_KEY` from the
|
|
180
|
-
agent's environment. Directories that `init` did not create are left untouched.
|
|
181
|
-
After a CLI upgrade, the next command refreshes the installed skill automatically.
|
|
182
|
-
Set `ASSETHUB_CLI_HOME` to redirect all of these writes, for example in tests.
|
|
224
|
+
To write these settings into each agent for you, run `assethub setup` (see
|
|
225
|
+
Quick setup above).
|
|
183
226
|
|
|
184
227
|
Explicit `--profile` uses that profile's key and origin, ahead of environment
|
|
185
228
|
variables, including user tokens and workspace MFA proofs. A missing profile fails.
|
package/dist/agentSetup.d.ts
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
export declare const AGENT_IDS: readonly ["claude-code", "codex", "cursor"];
|
|
2
2
|
export type AgentId = (typeof AGENT_IDS)[number];
|
|
3
|
-
type AgentSpec = {
|
|
3
|
+
export type AgentSpec = {
|
|
4
4
|
id: AgentId;
|
|
5
5
|
name: string;
|
|
6
|
-
/**
|
|
6
|
+
/** The command on PATH; finding it, or `detectDir` under home, means the agent is installed. */
|
|
7
|
+
binary: string;
|
|
7
8
|
detectDir: string;
|
|
8
|
-
globalMcpPath: string;
|
|
9
|
-
projectMcpPath: string;
|
|
10
9
|
skillDir: string;
|
|
11
10
|
format: 'claude' | 'cursor' | 'codex';
|
|
12
11
|
};
|
|
12
|
+
export declare const AGENT_SPECS: readonly AgentSpec[];
|
|
13
|
+
export declare const agentSpec: (id: AgentId) => AgentSpec;
|
|
14
|
+
/** `--agent` values: repeatable or comma separated; `claude` is accepted for `claude-code`. Keeps AGENT_IDS order. */
|
|
15
|
+
export declare const parseAgentIds: (values: string[]) => AgentId[];
|
|
16
|
+
/** Agents installed here: their folder exists under home (or $CODEX_HOME for Codex), or their command is on PATH. */
|
|
17
|
+
export declare const detectAgents: (home: string, findBinary: (name: string) => Promise<string | undefined>, codexHome?: string) => Promise<AgentId[]>;
|
|
13
18
|
/** `ASSETHUB_CLI_HOME` keeps tests and sandboxes from writing into the real home directory. */
|
|
14
19
|
export declare const cliHome: () => string;
|
|
15
20
|
export declare const packagedSkillDir: () => string;
|
|
16
21
|
export declare const canonicalSkillDir: (home: string) => string;
|
|
17
22
|
export declare const mcpServerEntry: (format: AgentSpec["format"], baseUrl: string, workspaceId?: string) => Record<string, unknown>;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/** The `mcpServers.assethub` entry of a JSON config, or undefined when the file has none or cannot be read. */
|
|
24
|
+
export declare const readJsonServer: (text: string | undefined) => unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Sets `mcpServers.assethub` in a JSON config and keeps every other key.
|
|
27
|
+
* Returns the new text, or the input unchanged when the entry already matches.
|
|
28
|
+
*/
|
|
29
|
+
export declare const mergeJsonServer: (path: string, text: string | undefined, entry: Record<string, unknown>) => string;
|
|
30
|
+
export type SkillStatus = 'installed' | 'updated' | 'unchanged' | 'kept-existing' | 'kept-newer';
|
|
31
|
+
export declare const installSkill: ({ root, version, dryRun, }: {
|
|
32
|
+
/** The home folder, or the project folder for a project install. */
|
|
33
|
+
root: string;
|
|
22
34
|
version: string;
|
|
23
35
|
dryRun: boolean;
|
|
24
36
|
}) => Promise<{
|
|
@@ -26,44 +38,27 @@ export declare const installSkill: ({ home, version, dryRun, }: {
|
|
|
26
38
|
status: SkillStatus;
|
|
27
39
|
version?: string;
|
|
28
40
|
}>;
|
|
29
|
-
type LinkStatus = 'linked' | 'relinked' | 'unchanged' | 'kept-existing';
|
|
41
|
+
export type LinkStatus = 'linked' | 'relinked' | 'unchanged' | 'kept-existing';
|
|
30
42
|
/**
|
|
31
43
|
* Refresh an installed skill after a CLI upgrade. Runs before every command;
|
|
32
44
|
* the steady-state cost is one small file read. Never throws — a read-only or
|
|
33
45
|
* missing home must not fail the user's actual command.
|
|
34
46
|
*/
|
|
35
47
|
export declare const syncInstalledSkill: () => Promise<void>;
|
|
36
|
-
export type
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
cwd?: string;
|
|
45
|
-
home?: string;
|
|
46
|
-
};
|
|
47
|
-
export type InitReport = {
|
|
48
|
-
version: string;
|
|
49
|
-
dryRun: boolean;
|
|
50
|
-
scope: 'global' | 'project';
|
|
51
|
-
skill: Awaited<ReturnType<typeof installSkill>>;
|
|
52
|
-
agents: {
|
|
53
|
-
id: AgentId;
|
|
54
|
-
name: string;
|
|
55
|
-
detected: boolean;
|
|
56
|
-
mcp: {
|
|
57
|
-
path: string;
|
|
58
|
-
status: WriteStatus;
|
|
59
|
-
};
|
|
60
|
-
skill: {
|
|
61
|
-
path: string;
|
|
62
|
-
status: LinkStatus;
|
|
63
|
-
};
|
|
48
|
+
export type SkillsReport = {
|
|
49
|
+
path: string;
|
|
50
|
+
status: SkillStatus;
|
|
51
|
+
version?: string;
|
|
52
|
+
links: {
|
|
53
|
+
agent: AgentId;
|
|
54
|
+
path: string;
|
|
55
|
+
status: LinkStatus;
|
|
64
56
|
}[];
|
|
65
|
-
next: string[];
|
|
66
57
|
};
|
|
67
|
-
|
|
68
|
-
export {}
|
|
58
|
+
/** Installs the skill under `<root>/.agents/skills` and links it into each agent's skill folder under root. */
|
|
59
|
+
export declare const installAgentSkills: ({ root, agents, dryRun, }: {
|
|
60
|
+
root: string;
|
|
61
|
+
agents: AgentId[];
|
|
62
|
+
dryRun: boolean;
|
|
63
|
+
}) => Promise<SkillsReport>;
|
|
69
64
|
//# sourceMappingURL=agentSetup.d.ts.map
|
package/dist/agentSetup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentSetup.d.ts","sourceRoot":"","sources":["../src/agentSetup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agentSetup.d.ts","sourceRoot":"","sources":["../src/agentSetup.ts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,SAAS,6CAA8C,CAAA;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACtC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAyB3C,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,IAAI,OAAO,KAAG,SACiB,CAAA;AAEzD,sHAAsH;AACtH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,KAAG,OAAO,EAOvD,CAAA;AAED,qHAAqH;AACrH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,YAAY,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EACzD,YAAY,MAAM,KACjB,OAAO,CAAC,OAAO,EAAE,CAUnB,CAAA;AAMD,+FAA+F;AAC/F,eAAO,MAAM,OAAO,QAAO,MAA4C,CAAA;AAEvE,eAAO,MAAM,gBAAgB,QAAO,MACiC,CAAA;AAErE,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MACJ,CAAA;AA2B7C,eAAO,MAAM,cAAc,GACzB,QAAQ,SAAS,CAAC,QAAQ,CAAC,EAC3B,SAAS,MAAM,EACf,cAAc,MAAM,KACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAYxB,CAAA;AAED,+GAA+G;AAC/G,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,OAQzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EACZ,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MA2BF,CAAA;AAID,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,eAAe,GAAG,YAAY,CAAA;AAmChG,eAAO,MAAM,YAAY,GAAU,4BAIhC;IACD,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB,KAAG,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,CA2BhE,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,eAAe,CAAA;AAkC9E;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CAgBvD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAC,EAAE,CAAA;CAC5D,CAAA;AAED,+GAA+G;AAC/G,eAAO,MAAM,kBAAkB,GAAU,2BAItC;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;CAChB,KAAG,OAAO,CAAC,YAAY,CAMvB,CAAA"}
|
package/dist/agentSetup.js
CHANGED
|
@@ -1,53 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The agent-facing half of `assethub setup`: which coding agents are on this
|
|
3
|
+
* machine, where their MCP config lives, and the skill they read on demand.
|
|
3
4
|
*
|
|
4
5
|
* Coding agents learn a tool from two local files: an MCP server entry in their
|
|
5
|
-
* own config, and a skill directory
|
|
6
|
-
*
|
|
7
|
-
* rather than replacing it, and never touching credentials — the MCP entries
|
|
6
|
+
* own config, and a skill directory. `setup` writes both, merging into existing
|
|
7
|
+
* config rather than replacing it and never writing credentials: the MCP entries
|
|
8
8
|
* reference `ASSETHUB_API_KEY` from the agent's environment.
|
|
9
9
|
*
|
|
10
|
-
* The skill is copied once to
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* The skill is copied once to `<root>/.agents/skills/assethub` (root is the home
|
|
11
|
+
* folder, or the project with --project) and symlinked into each agent's skill
|
|
12
|
+
* directory. A version marker lets every later command refresh the home copy
|
|
13
|
+
* when the CLI has been upgraded, so agents never read rules written for a
|
|
14
|
+
* version they are no longer running.
|
|
14
15
|
*/
|
|
15
16
|
import { cp, lstat, mkdir, readFile, readlink, rename, rm, stat, symlink, writeFile, } from 'node:fs/promises';
|
|
16
17
|
import { homedir } from 'node:os';
|
|
17
18
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
18
|
-
import {
|
|
19
|
+
import { env, pid } from 'node:process';
|
|
19
20
|
import { fileURLToPath } from 'node:url';
|
|
20
|
-
import { cliVersion,
|
|
21
|
+
import { cliVersion, validatedBaseUrl } from './setup.js';
|
|
22
|
+
import { compareVersions } from './update.js';
|
|
21
23
|
export const AGENT_IDS = ['claude-code', 'codex', 'cursor'];
|
|
22
|
-
const
|
|
24
|
+
export const AGENT_SPECS = [
|
|
23
25
|
{
|
|
24
26
|
id: 'claude-code',
|
|
25
27
|
name: 'Claude Code',
|
|
28
|
+
binary: 'claude',
|
|
26
29
|
detectDir: '.claude',
|
|
27
|
-
globalMcpPath: '.claude.json',
|
|
28
|
-
projectMcpPath: '.mcp.json',
|
|
29
30
|
skillDir: '.claude/skills',
|
|
30
31
|
format: 'claude',
|
|
31
32
|
},
|
|
32
33
|
{
|
|
33
34
|
id: 'codex',
|
|
34
35
|
name: 'Codex',
|
|
36
|
+
binary: 'codex',
|
|
35
37
|
detectDir: '.codex',
|
|
36
|
-
globalMcpPath: '.codex/config.toml',
|
|
37
|
-
projectMcpPath: '.codex/config.toml',
|
|
38
38
|
skillDir: '.codex/skills',
|
|
39
39
|
format: 'codex',
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
id: 'cursor',
|
|
43
43
|
name: 'Cursor',
|
|
44
|
+
binary: 'cursor',
|
|
44
45
|
detectDir: '.cursor',
|
|
45
|
-
globalMcpPath: '.cursor/mcp.json',
|
|
46
|
-
projectMcpPath: '.cursor/mcp.json',
|
|
47
46
|
skillDir: '.cursor/skills',
|
|
48
47
|
format: 'cursor',
|
|
49
48
|
},
|
|
50
49
|
];
|
|
50
|
+
export const agentSpec = (id) => AGENT_SPECS.find(agent => agent.id === id);
|
|
51
|
+
/** `--agent` values: repeatable or comma separated; `claude` is accepted for `claude-code`. Keeps AGENT_IDS order. */
|
|
52
|
+
export const parseAgentIds = (values) => {
|
|
53
|
+
const names = values.flatMap(value => value.split(',')).map(value => value.trim().toLowerCase()).filter(Boolean);
|
|
54
|
+
const ids = names.map(name => (name === 'claude' ? 'claude-code' : name));
|
|
55
|
+
const unknown = ids.filter(id => !AGENT_IDS.includes(id));
|
|
56
|
+
if (unknown.length > 0)
|
|
57
|
+
throw new Error(`Unknown agent: ${unknown.join(', ')}. Use --agent ${AGENT_IDS.join('|')}.`);
|
|
58
|
+
return AGENT_IDS.filter(id => ids.includes(id));
|
|
59
|
+
};
|
|
60
|
+
/** Agents installed here: their folder exists under home (or $CODEX_HOME for Codex), or their command is on PATH. */
|
|
61
|
+
export const detectAgents = async (home, findBinary, codexHome) => {
|
|
62
|
+
const found = await Promise.all(AGENT_SPECS.map(async (agent) => (await isDirectory(join(home, agent.detectDir))) ||
|
|
63
|
+
(agent.id === 'codex' && codexHome !== undefined && (await isDirectory(codexHome))) ||
|
|
64
|
+
(await findBinary(agent.binary)) !== undefined));
|
|
65
|
+
return AGENT_SPECS.filter((_, index) => found[index]).map(agent => agent.id);
|
|
66
|
+
};
|
|
51
67
|
const SERVER_NAME = 'assethub';
|
|
52
68
|
const SKILL_NAME = 'assethub';
|
|
53
69
|
const VERSION_MARKER = '.assethub-cli-version';
|
|
@@ -92,16 +108,31 @@ export const mcpServerEntry = (format, baseUrl, workspaceId) => {
|
|
|
92
108
|
},
|
|
93
109
|
};
|
|
94
110
|
};
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
/** The `mcpServers.assethub` entry of a JSON config, or undefined when the file has none or cannot be read. */
|
|
112
|
+
export const readJsonServer = (text) => {
|
|
113
|
+
if (text === undefined)
|
|
114
|
+
return undefined;
|
|
115
|
+
try {
|
|
116
|
+
const parsed = JSON.parse(text);
|
|
117
|
+
return parsed?.mcpServers?.[SERVER_NAME];
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Sets `mcpServers.assethub` in a JSON config and keeps every other key.
|
|
125
|
+
* Returns the new text, or the input unchanged when the entry already matches.
|
|
126
|
+
*/
|
|
127
|
+
export const mergeJsonServer = (path, text, entry) => {
|
|
97
128
|
let document = {};
|
|
98
|
-
if (text !== undefined) {
|
|
129
|
+
if (text !== undefined && text.trim() !== '') {
|
|
99
130
|
let parsed;
|
|
100
131
|
try {
|
|
101
132
|
parsed = JSON.parse(text);
|
|
102
133
|
}
|
|
103
134
|
catch {
|
|
104
|
-
throw new Error(`${path} is not valid JSON. Fix or move it, then rerun
|
|
135
|
+
throw new Error(`${path} is not valid JSON. Fix or move it, then rerun setup.`);
|
|
105
136
|
}
|
|
106
137
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
107
138
|
throw new Error(`${path} must contain a JSON object.`);
|
|
@@ -112,45 +143,11 @@ const writeJsonServer = async (path, entry, dryRun) => {
|
|
|
112
143
|
(typeof document.mcpServers !== 'object' ||
|
|
113
144
|
document.mcpServers === null ||
|
|
114
145
|
Array.isArray(document.mcpServers)))
|
|
115
|
-
throw new Error(`${path} has a non-object "mcpServers" value. Fix or move it, then rerun
|
|
146
|
+
throw new Error(`${path} has a non-object "mcpServers" value. Fix or move it, then rerun setup.`);
|
|
116
147
|
const servers = (document.mcpServers ?? {});
|
|
117
|
-
if (JSON.stringify(servers[SERVER_NAME]) === JSON.stringify(entry))
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
if (!dryRun) {
|
|
121
|
-
await mkdir(dirname(path), { recursive: true });
|
|
122
|
-
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
123
|
-
}
|
|
124
|
-
return text === undefined ? 'created' : 'updated';
|
|
125
|
-
};
|
|
126
|
-
/** Replaces or appends one `[mcp_servers.assethub]` table; every other line is kept verbatim. */
|
|
127
|
-
const writeTomlSection = async (path, section, dryRun) => {
|
|
128
|
-
const header = `[mcp_servers.${SERVER_NAME}]`;
|
|
129
|
-
const body = section.trimEnd();
|
|
130
|
-
const text = await readText(path);
|
|
131
|
-
const lines = (text ?? '').split('\n');
|
|
132
|
-
const start = lines.findIndex(line => line.trim() === header);
|
|
133
|
-
let next;
|
|
134
|
-
if (start === -1) {
|
|
135
|
-
const base = (text ?? '').trimEnd();
|
|
136
|
-
next = `${base ? `${base}\n\n` : ''}${body}\n`;
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
const endOffset = lines
|
|
140
|
-
.slice(start + 1)
|
|
141
|
-
.findIndex(line => /^\s*\[/.test(line));
|
|
142
|
-
const end = endOffset === -1 ? lines.length : start + 1 + endOffset;
|
|
143
|
-
if (lines.slice(start, end).join('\n').trimEnd() === body)
|
|
144
|
-
return 'unchanged';
|
|
145
|
-
const before = lines.slice(0, start).join('\n').trimEnd();
|
|
146
|
-
const after = lines.slice(end).join('\n').trim();
|
|
147
|
-
next = `${before ? `${before}\n\n` : ''}${body}\n${after ? `\n${after}\n` : ''}`;
|
|
148
|
-
}
|
|
149
|
-
if (!dryRun) {
|
|
150
|
-
await mkdir(dirname(path), { recursive: true });
|
|
151
|
-
await writeFile(path, next);
|
|
152
|
-
}
|
|
153
|
-
return text === undefined ? 'created' : 'updated';
|
|
148
|
+
if (text !== undefined && JSON.stringify(servers[SERVER_NAME]) === JSON.stringify(entry))
|
|
149
|
+
return text;
|
|
150
|
+
return `${JSON.stringify({ ...document, mcpServers: { ...servers, [SERVER_NAME]: entry } }, null, 2)}\n`;
|
|
154
151
|
};
|
|
155
152
|
const readMarker = async (dir) => (await readText(join(dir, VERSION_MARKER)))?.trim();
|
|
156
153
|
/**
|
|
@@ -182,11 +179,11 @@ const replaceSkillDir = async (source, target, version) => {
|
|
|
182
179
|
if (hadTarget)
|
|
183
180
|
await rm(previous, { recursive: true, force: true });
|
|
184
181
|
};
|
|
185
|
-
export const installSkill = async ({
|
|
182
|
+
export const installSkill = async ({ root, version, dryRun, }) => {
|
|
186
183
|
const source = packagedSkillDir();
|
|
187
184
|
if (!(await isDirectory(source)))
|
|
188
185
|
throw new Error('This CLI installation does not include the packaged agent skill.');
|
|
189
|
-
const target = canonicalSkillDir(
|
|
186
|
+
const target = canonicalSkillDir(root);
|
|
190
187
|
const marker = await readMarker(target);
|
|
191
188
|
// A directory without our marker is the user's own; leave it alone.
|
|
192
189
|
const status = marker === undefined
|
|
@@ -195,13 +192,16 @@ export const installSkill = async ({ home, version, dryRun, }) => {
|
|
|
195
192
|
: 'installed'
|
|
196
193
|
: marker === version
|
|
197
194
|
? 'unchanged'
|
|
198
|
-
:
|
|
195
|
+
: // An older CLI (or a stale monorepo build) never replaces a newer skill.
|
|
196
|
+
compareVersions(version, marker) < 0
|
|
197
|
+
? 'kept-newer'
|
|
198
|
+
: 'updated';
|
|
199
199
|
if (!dryRun && (status === 'installed' || status === 'updated'))
|
|
200
200
|
await replaceSkillDir(source, target, version);
|
|
201
201
|
return {
|
|
202
202
|
path: target,
|
|
203
203
|
status,
|
|
204
|
-
...(status === 'kept-existing' ? {} : { version }),
|
|
204
|
+
...(status === 'kept-existing' ? {} : { version: status === 'kept-newer' ? marker : version }),
|
|
205
205
|
};
|
|
206
206
|
};
|
|
207
207
|
const linkSkill = async (agentSkillsDir, canonical, dryRun) => {
|
|
@@ -248,7 +248,8 @@ export const syncInstalledSkill = async () => {
|
|
|
248
248
|
if (installed === undefined)
|
|
249
249
|
return;
|
|
250
250
|
const version = await cliVersion();
|
|
251
|
-
|
|
251
|
+
// Only an upgrade refreshes it: an older CLI never overwrites a newer skill.
|
|
252
|
+
if (installed === version || compareVersions(version, installed) < 0)
|
|
252
253
|
return;
|
|
253
254
|
const source = packagedSkillDir();
|
|
254
255
|
if (!(await isDirectory(source)))
|
|
@@ -259,53 +260,11 @@ export const syncInstalledSkill = async () => {
|
|
|
259
260
|
// best effort
|
|
260
261
|
}
|
|
261
262
|
};
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (unknown.length > 0)
|
|
270
|
-
throw new Error(`Unknown agent: ${unknown.join(', ')}. Use --agent ${AGENT_IDS.join('|')}.`);
|
|
271
|
-
const detected = new Map(await Promise.all(AGENTS.map(async (agent) => [agent.id, await isDirectory(join(home, agent.detectDir))])));
|
|
272
|
-
const selected = requested.length > 0
|
|
273
|
-
? AGENTS.filter(agent => requested.includes(agent.id))
|
|
274
|
-
: AGENTS.filter(agent => detected.get(agent.id));
|
|
275
|
-
if (selected.length === 0)
|
|
276
|
-
throw new Error(`No supported coding agent found under ${home}. Pass --agent ${AGENT_IDS.join('|')} to choose one.`);
|
|
277
|
-
const skill = await installSkill({ home, version, dryRun });
|
|
278
|
-
const agents = [];
|
|
279
|
-
for (const spec of selected) {
|
|
280
|
-
const mcpPath = options.project
|
|
281
|
-
? join(cwd, spec.projectMcpPath)
|
|
282
|
-
: join(home, spec.globalMcpPath);
|
|
283
|
-
const mcp = spec.format === 'codex'
|
|
284
|
-
? await writeTomlSection(mcpPath, mcpConfig('codex', options.baseUrl, false, options.workspaceId), dryRun)
|
|
285
|
-
: await writeJsonServer(mcpPath, mcpServerEntry(spec.format, options.baseUrl, options.workspaceId), dryRun);
|
|
286
|
-
const link = await linkSkill(join(home, spec.skillDir), skill.path, dryRun);
|
|
287
|
-
agents.push({
|
|
288
|
-
id: spec.id,
|
|
289
|
-
name: spec.name,
|
|
290
|
-
detected: detected.get(spec.id) ?? false,
|
|
291
|
-
mcp: { path: mcpPath, status: mcp },
|
|
292
|
-
skill: link,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
return {
|
|
296
|
-
version,
|
|
297
|
-
dryRun,
|
|
298
|
-
scope: options.project ? 'project' : 'global',
|
|
299
|
-
skill,
|
|
300
|
-
agents,
|
|
301
|
-
next: [
|
|
302
|
-
...(env.ASSETHUB_API_KEY
|
|
303
|
-
? []
|
|
304
|
-
: [
|
|
305
|
-
"Provide ASSETHUB_API_KEY in each agent's environment; the MCP entries read it from there and never store it.",
|
|
306
|
-
]),
|
|
307
|
-
'Restart the agent so it reloads MCP servers and skills.',
|
|
308
|
-
'Run `assethub doctor --mcp` to verify the connection.',
|
|
309
|
-
],
|
|
310
|
-
};
|
|
263
|
+
/** Installs the skill under `<root>/.agents/skills` and links it into each agent's skill folder under root. */
|
|
264
|
+
export const installAgentSkills = async ({ root, agents, dryRun, }) => {
|
|
265
|
+
const skill = await installSkill({ root, version: await cliVersion(), dryRun });
|
|
266
|
+
const links = [];
|
|
267
|
+
for (const agent of agents)
|
|
268
|
+
links.push({ agent, ...(await linkSkill(join(root, agentSpec(agent).skillDir), skill.path, dryRun)) });
|
|
269
|
+
return { ...skill, links };
|
|
311
270
|
};
|
package/dist/hooks/command.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type HooksCommandDeps = {
|
|
|
9
9
|
now?: () => Date;
|
|
10
10
|
startUpload?: (sessionDir: string, profile?: string) => void;
|
|
11
11
|
startSweep?: (profile?: string) => void;
|
|
12
|
+
startWorkingUpload?: (sessionDir: string, profile?: string) => void;
|
|
12
13
|
};
|
|
13
14
|
export declare const runHooksCommand: (argv: string[], deps?: HooksCommandDeps) => Promise<number>;
|
|
14
15
|
//# sourceMappingURL=command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/hooks/command.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5D,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/hooks/command.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5D,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpE,CAAA;AA+CD,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EAAE,EACd,OAAM,gBAAqB,KAC1B,OAAO,CAAC,MAAM,CAkIhB,CAAA"}
|
package/dist/hooks/command.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// runHooksCommand(argv).
|
|
3
3
|
import { installHooks, sessionSaveDisclosure, uninstallHooks } from './install.js';
|
|
4
4
|
import { listSessionDirs, readMeta } from './paths.js';
|
|
5
|
-
import { saveSession } from './save.js';
|
|
5
|
+
import { saveSession, spawnDetachedSessionUpload } from './save.js';
|
|
6
6
|
import { canUploadSessions, resolveUploadAuth, uploadPending, uploadSession } from './upload.js';
|
|
7
7
|
const USAGE = 'Usage: assethub hooks <install|uninstall|save|upload|list|status> [--client claude|codex] [--json]\n' +
|
|
8
8
|
' install|uninstall [--global] (the current folder by default; --global for every project)\n' +
|
|
@@ -118,6 +118,7 @@ export const runHooksCommand = async (argv, deps = {}) => {
|
|
|
118
118
|
upload: { profile: uploadOptions.profile },
|
|
119
119
|
startUpload: deps.startUpload,
|
|
120
120
|
startSweep: deps.startSweep,
|
|
121
|
+
startWorkingUpload: deps.startWorkingUpload ?? spawnDetachedSessionUpload,
|
|
121
122
|
});
|
|
122
123
|
// A hook must never fail the agent, so save always exits 0.
|
|
123
124
|
// Claude Code adds SessionStart hook output to the model's context, so
|
package/dist/hooks/save.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export type SaveSessionOptions = {
|
|
|
22
22
|
startUpload?: (sessionDir: string, profile?: string) => void;
|
|
23
23
|
/** Starts the background retry of pending sessions on SessionStart. */
|
|
24
24
|
startSweep?: (profile?: string) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Starts `assethub hooks upload --session <dir> --auto` for a session that is
|
|
27
|
+
* still running (Stop, PreCompact). Omitted: no upload while working. The
|
|
28
|
+
* hooks command passes spawnDetachedSessionUpload; tests pass a stub.
|
|
29
|
+
*/
|
|
30
|
+
startWorkingUpload?: (sessionDir: string, profile?: string) => void;
|
|
25
31
|
};
|
|
26
32
|
export type SaveSessionResult = {
|
|
27
33
|
sessionDir: string;
|
|
@@ -33,6 +39,8 @@ export type SaveSessionResult = {
|
|
|
33
39
|
};
|
|
34
40
|
/** SessionEnd: upload this session, then retry up to SWEEP_LIMIT older pending ones. */
|
|
35
41
|
export declare const spawnDetachedUpload: (sessionDir: string, profile?: string) => void;
|
|
42
|
+
/** Stop / PreCompact: upload this running session only; no sweep. */
|
|
43
|
+
export declare const spawnDetachedSessionUpload: (sessionDir: string, profile?: string) => void;
|
|
36
44
|
/** SessionStart: retry up to SWEEP_LIMIT pending sessions. */
|
|
37
45
|
export declare const spawnDetachedSweep: (profile?: string) => void;
|
|
38
46
|
export declare const retentionDays: (env: NodeJS.ProcessEnv) => number | null;
|
|
@@ -42,6 +50,12 @@ export declare const pruneSessions: (root: string, now: Date, days: number, keep
|
|
|
42
50
|
export declare const ABANDONED_AFTER_MS: number;
|
|
43
51
|
/** Sessions retried in the background per SessionStart / SessionEnd. */
|
|
44
52
|
export declare const SWEEP_LIMIT = 3;
|
|
53
|
+
/**
|
|
54
|
+
* A running session is uploaded at most this often. Claude Code app sessions
|
|
55
|
+
* stay open for days and rarely reach SessionEnd, so without this they would
|
|
56
|
+
* only be uploaded once abandoned (ABANDONED_AFTER_MS quiet, then a new session).
|
|
57
|
+
*/
|
|
58
|
+
export declare const WORKING_UPLOAD_INTERVAL_MS: number;
|
|
45
59
|
/**
|
|
46
60
|
* Copy + redact the transcript Claude Code keeps at `meta.transcriptPath`, and
|
|
47
61
|
* collect its images. Runs once per SessionEnd and before an upload, never per
|
package/dist/hooks/save.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/hooks/save.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAgB,WAAW,EAAC,MAAM,YAAY,CAAA;AAChF,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAE9C,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,eAAO,MAAM,eAAe,QAAmB,CAAA;AAC/C,eAAO,MAAM,sBAAsB,KAAK,CAAA;AASxC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,MAAM,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5D,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/hooks/save.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAgB,WAAW,EAAC,MAAM,YAAY,CAAA;AAChF,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAE9C,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,eAAO,MAAM,eAAe,QAAmB,CAAA;AAC/C,eAAO,MAAM,sBAAsB,KAAK,CAAA;AASxC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,MAAM,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5D,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpE,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAA;CACjC,CAAA;AAYD,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,EAAE,UAAU,MAAM,KAAG,IACgC,CAAA;AAE3G,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,GAAI,YAAY,MAAM,EAAE,UAAU,MAAM,KAAG,IACP,CAAA;AAE3E,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,IAC+B,CAAA;AAErF,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,CAAC,UAAU,KAAG,MAAM,GAAG,IAM/D,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,KAAK,IAAI,EACT,MAAM,MAAM,EACZ,MAAM,MAAM,KACX,OAAO,CAAC,MAAM,CAwBhB,CAAA;AAgFD,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,QAAqB,CAAA;AACpD,wEAAwE;AACxE,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,QAAiB,CAAA;AAMxD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,UAAS;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAM,KAC/D,OAAO,CAAC,WAAW,GAAG,IAAI,CA6C5B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,KAAK,IAAI,EACT,mBAAmB,MAAM,KACxB,OAAO,CAAC,MAAM,CAmBhB,CAAA;AAED,eAAO,MAAM,WAAW,GAAU,UAAS,kBAAuB,KAAG,OAAO,CAAC,iBAAiB,CAgG7F,CAAA"}
|