@sanity/cli 7.2.3 → 7.3.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/bin/run.js +3 -12
- package/dist/actions/auth/authServer.js +37 -20
- package/dist/actions/auth/authServer.js.map +1 -1
- package/dist/actions/backup/assertDatasetExist.js +3 -3
- package/dist/actions/backup/assertDatasetExist.js.map +1 -1
- package/dist/actions/build/buildApp.js +15 -5
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStaticFiles.js +57 -5
- package/dist/actions/build/buildStaticFiles.js.map +1 -1
- package/dist/actions/build/buildStudio.js +13 -5
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/handlePrereleaseVersions.js +2 -2
- package/dist/actions/build/handlePrereleaseVersions.js.map +1 -1
- package/dist/actions/dataset/resolveDataset.js +2 -2
- package/dist/actions/dataset/resolveDataset.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +43 -5
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +14 -2
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/viewDeployment.js +32 -0
- package/dist/actions/deploy/viewDeployment.js.map +1 -0
- package/dist/actions/dev/devAction.js +219 -4
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/dev/registration/deriveInterfaces.js +46 -0
- package/dist/actions/dev/registration/deriveInterfaces.js.map +1 -0
- package/dist/actions/dev/registration/extractDevServerManifest.js +30 -0
- package/dist/actions/dev/registration/extractDevServerManifest.js.map +1 -0
- package/dist/actions/dev/registration/interfaceSetId.js +41 -0
- package/dist/actions/dev/registration/interfaceSetId.js.map +1 -0
- package/dist/actions/dev/registration/startDevManifestWatcher.js +104 -0
- package/dist/actions/dev/registration/startDevManifestWatcher.js.map +1 -0
- package/dist/actions/dev/registration/startDevServerRegistration.js +121 -0
- package/dist/actions/dev/registration/startDevServerRegistration.js.map +1 -0
- package/dist/actions/dev/servers/getDashboardAppUrl.js.map +1 -0
- package/dist/actions/dev/servers/getDevServerConfig.js +45 -0
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -0
- package/dist/actions/dev/servers/startAppDevServer.js +78 -0
- package/dist/actions/dev/servers/startAppDevServer.js.map +1 -0
- package/dist/actions/dev/{startStudioDevServer.js → servers/startStudioDevServer.js} +20 -16
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -0
- package/dist/actions/dev/types.js +7 -1
- package/dist/actions/dev/types.js.map +1 -1
- package/dist/actions/dev/workbench/startWorkbenchDevServer.js +276 -0
- package/dist/actions/dev/workbench/startWorkbenchDevServer.js.map +1 -0
- package/dist/actions/dev/workbench/writeWorkbenchRuntime.js +66 -0
- package/dist/actions/dev/workbench/writeWorkbenchRuntime.js.map +1 -0
- package/dist/actions/init/bootstrapLocalTemplate.js +15 -3
- package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
- package/dist/actions/init/bootstrapTemplate.js +3 -2
- package/dist/actions/init/bootstrapTemplate.js.map +1 -1
- package/dist/actions/init/createAppCliConfig.js +5 -2
- package/dist/actions/init/createAppCliConfig.js.map +1 -1
- package/dist/actions/init/createCliConfig.js +5 -3
- package/dist/actions/init/createCliConfig.js.map +1 -1
- package/dist/actions/init/createStudioConfig.js.map +1 -1
- package/dist/actions/init/initAction.js +11 -4
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/init/initApp.js +2 -1
- package/dist/actions/init/initApp.js.map +1 -1
- package/dist/actions/init/initStudio.js +2 -1
- package/dist/actions/init/initStudio.js.map +1 -1
- package/dist/actions/init/scaffoldTemplate.js +3 -2
- package/dist/actions/init/scaffoldTemplate.js.map +1 -1
- package/dist/actions/init/templates/pageBuilder.js +1 -1
- package/dist/actions/init/templates/pageBuilder.js.map +1 -1
- package/dist/actions/init/types.js +1 -0
- package/dist/actions/init/types.js.map +1 -1
- package/dist/actions/manifest/{extractAppManifest.js → extractCoreAppManifest.js} +30 -11
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -0
- package/dist/actions/manifest/extractManifest.js +7 -7
- package/dist/actions/manifest/extractManifest.js.map +1 -1
- package/dist/actions/manifest/iconResolver.js +2 -3
- package/dist/actions/manifest/iconResolver.js.map +1 -1
- package/dist/actions/manifest/sanitizeIcon.js +13 -0
- package/dist/actions/manifest/sanitizeIcon.js.map +1 -0
- package/dist/actions/manifest/types.js +4 -9
- package/dist/actions/manifest/types.js.map +1 -1
- package/dist/actions/manifest/writeManifestFile.js +1 -1
- package/dist/actions/manifest/writeManifestFile.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +26 -2
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +24 -25
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/types.js.map +1 -1
- package/dist/actions/skills/configureSkills.js +49 -0
- package/dist/actions/skills/configureSkills.js.map +1 -0
- package/dist/actions/skills/readSkillState.js +40 -5
- package/dist/actions/skills/readSkillState.js.map +1 -1
- package/dist/actions/skills/setupSkills.js +47 -6
- package/dist/actions/skills/setupSkills.js.map +1 -1
- package/dist/actions/skills/skillCandidates.js +40 -0
- package/dist/actions/skills/skillCandidates.js.map +1 -0
- package/dist/actions/telemetry/telemetryDisclosure.js +2 -3
- package/dist/actions/telemetry/telemetryDisclosure.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +3 -3
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/commands/backups/disable.js +1 -1
- package/dist/commands/backups/disable.js.map +1 -1
- package/dist/commands/backups/download.js +1 -1
- package/dist/commands/backups/download.js.map +1 -1
- package/dist/commands/backups/enable.js +1 -1
- package/dist/commands/backups/enable.js.map +1 -1
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/embeddings/disable.js +1 -0
- package/dist/commands/datasets/embeddings/disable.js.map +1 -1
- package/dist/commands/datasets/embeddings/enable.js +1 -0
- package/dist/commands/datasets/embeddings/enable.js.map +1 -1
- package/dist/commands/datasets/embeddings/status.js +1 -0
- package/dist/commands/datasets/embeddings/status.js.map +1 -1
- package/dist/commands/dev.js +11 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +7 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/manifest/extract.js +7 -2
- package/dist/commands/manifest/extract.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -0
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/skills/install.js +40 -0
- package/dist/commands/skills/install.js.map +1 -0
- package/dist/commands/tokens/add.js +1 -1
- package/dist/commands/tokens/add.js.map +1 -1
- package/dist/exports/index.d.ts +6 -0
- package/dist/exports/index.js +8 -0
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/runtime.d.ts +41 -0
- package/dist/exports/runtime.js +13 -0
- package/dist/exports/runtime.js.map +1 -0
- package/dist/hooks/prerun/setupTelemetry.js +3 -1
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/server/devServer.js +8 -3
- package/dist/server/devServer.js.map +1 -1
- package/dist/services/documents.js +0 -1
- package/dist/services/documents.js.map +1 -1
- package/dist/telemetry/skills.telemetry.js +8 -0
- package/dist/telemetry/skills.telemetry.js.map +1 -0
- package/dist/util/compareDependencyVersions.js +1 -1
- package/dist/util/compareDependencyVersions.js.map +1 -1
- package/dist/util/determineIsApp.js +12 -1
- package/dist/util/determineIsApp.js.map +1 -1
- package/dist/util/getSharedServerConfig.js +2 -1
- package/dist/util/getSharedServerConfig.js.map +1 -1
- package/dist/util/resolveReactStrictMode.js +11 -0
- package/dist/util/resolveReactStrictMode.js.map +1 -0
- package/oclif.config.js +2 -1
- package/oclif.manifest.json +146 -113
- package/package.json +26 -17
- package/templates/page-builder/schemaTypes/index.js +1 -1
- package/dist/actions/build/checkRequiredDependencies.js +0 -124
- package/dist/actions/build/checkRequiredDependencies.js.map +0 -1
- package/dist/actions/build/getAutoUpdatesImportMap.js +0 -66
- package/dist/actions/build/getAutoUpdatesImportMap.js.map +0 -1
- package/dist/actions/dev/getDashboardAppUrl.js.map +0 -1
- package/dist/actions/dev/getDevServerConfig.js +0 -32
- package/dist/actions/dev/getDevServerConfig.js.map +0 -1
- package/dist/actions/dev/startAppDevServer.js +0 -59
- package/dist/actions/dev/startAppDevServer.js.map +0 -1
- package/dist/actions/dev/startStudioDevServer.js.map +0 -1
- package/dist/actions/manifest/extractAppManifest.js.map +0 -1
- /package/dist/actions/dev/{getDashboardAppUrl.js → servers/getDashboardAppUrl.js} +0 -0
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import { subdebug } from '@sanity/cli-core';
|
|
2
2
|
import { execa } from 'execa';
|
|
3
|
+
import { getSkillsCliAgentDisplayName, isUniversalSkillsCliAgentByEditorName, UNIVERSAL_SKILLS_DIR } from '../mcp/editorConfigs.js';
|
|
3
4
|
import { SKILLS_BIN_PATH } from './setupSkills.js';
|
|
4
5
|
const debug = subdebug('skills:state');
|
|
6
|
+
/** Normalizes path separators to `/` so comparisons are OS-agnostic. */ function toPosixPath(value) {
|
|
7
|
+
return value.replaceAll('\\', '/');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A skill lives in the shared universal skills directory when its canonical
|
|
11
|
+
* path is under `.agents/skills`. The `skills` CLI installs universal-agent
|
|
12
|
+
* skills there directly (rather than under an agent-specific directory).
|
|
13
|
+
*/ function isUniversalSkillPath(skillPath) {
|
|
14
|
+
if (typeof skillPath !== 'string') return false;
|
|
15
|
+
return toPosixPath(skillPath).includes(`${UNIVERSAL_SKILLS_DIR}/`);
|
|
16
|
+
}
|
|
5
17
|
/**
|
|
6
18
|
* Runs the bundled `skills list -g --json` and returns the set of agent
|
|
7
19
|
* display names that have every skill in `skillNames` installed globally.
|
|
8
20
|
* Agents missing any of the skills are excluded, so they get a (idempotent)
|
|
9
21
|
* re-install that fills the gap.
|
|
10
22
|
*
|
|
23
|
+
* "Universal" agents (those whose `skillsDir` is the shared `.agents/skills`
|
|
24
|
+
* directory) are credited whenever every requested skill lives in that shared
|
|
25
|
+
* directory. `skills list` only attributes a skill to agents it independently
|
|
26
|
+
* detects, so a universal editor we detected may be absent from the output even
|
|
27
|
+
* though it reads from the shared directory and already has the skills.
|
|
28
|
+
*
|
|
11
29
|
* Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and
|
|
12
30
|
* resolved with an empty set. Callers should treat that as "treat all agents
|
|
13
31
|
* as not installed" — re-installing is idempotent, so over-installing is
|
|
14
32
|
* safer than skipping based on a flaky probe.
|
|
15
33
|
*/ export async function readSkillState(opts) {
|
|
34
|
+
const { editors, skillNames } = opts;
|
|
16
35
|
const empty = {
|
|
17
36
|
installedAgentDisplayNames: new Set()
|
|
18
37
|
};
|
|
@@ -43,18 +62,34 @@ const debug = subdebug('skills:state');
|
|
|
43
62
|
debug('Unexpected skills list JSON shape (not an array)');
|
|
44
63
|
return empty;
|
|
45
64
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
65
|
+
const entries = parsed;
|
|
66
|
+
const agentSets = skillNames.map((skillName)=>{
|
|
67
|
+
const match = entries.find((entry)=>entry?.name === skillName);
|
|
48
68
|
if (!match || !Array.isArray(match.agents)) {
|
|
49
69
|
return new Set();
|
|
50
70
|
}
|
|
51
71
|
return new Set(match.agents.filter((a)=>typeof a === 'string'));
|
|
52
72
|
});
|
|
53
|
-
const
|
|
73
|
+
const installed = new Set([
|
|
54
74
|
...agentSets[0] ?? []
|
|
55
|
-
].filter((agent)=>agentSets.every((set)=>set.has(agent)));
|
|
75
|
+
].filter((agent)=>agentSets.every((set)=>set.has(agent))));
|
|
76
|
+
// Universal agents share the `.agents/skills` directory. If every requested
|
|
77
|
+
// skill lives there, credit each detected universal editor — otherwise a
|
|
78
|
+
// detected universal editor that the `skills` CLI didn't independently detect
|
|
79
|
+
// would be treated as missing and trigger a redundant re-install.
|
|
80
|
+
const allSkillsInUniversalDir = skillNames.length > 0 && skillNames.every((skillName)=>{
|
|
81
|
+
const match = entries.find((entry)=>entry?.name === skillName);
|
|
82
|
+
return match ? isUniversalSkillPath(match.path) : false;
|
|
83
|
+
});
|
|
84
|
+
if (allSkillsInUniversalDir) {
|
|
85
|
+
for (const editor of editors){
|
|
86
|
+
if (!isUniversalSkillsCliAgentByEditorName(editor.name)) continue;
|
|
87
|
+
const displayName = getSkillsCliAgentDisplayName(editor.name);
|
|
88
|
+
if (displayName) installed.add(displayName);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
56
91
|
return {
|
|
57
|
-
installedAgentDisplayNames:
|
|
92
|
+
installedAgentDisplayNames: installed
|
|
58
93
|
};
|
|
59
94
|
}
|
|
60
95
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/skills/readSkillState.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {execa} from 'execa'\n\nimport {SKILLS_BIN_PATH} from './setupSkills.js'\n\nconst debug = subdebug('skills:state')\n\ninterface ReadSkillStateOptions {\n /** Names of the skills to look up (match the `name` field in `skills list --json`). */\n skillNames: string[]\n}\n\ninterface SkillState {\n /** Display names of agents that have all of these skills installed globally. */\n installedAgentDisplayNames: Set<string>\n}\n\ninterface SkillListEntry {\n agents?: unknown\n name?: unknown\n}\n\n/**\n * Runs the bundled `skills list -g --json` and returns the set of agent\n * display names that have every skill in `skillNames` installed globally.\n * Agents missing any of the skills are excluded, so they get a (idempotent)\n * re-install that fills the gap.\n *\n * Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and\n * resolved with an empty set. Callers should treat that as \"treat all agents\n * as not installed\" — re-installing is idempotent, so over-installing is\n * safer than skipping based on a flaky probe.\n */\nexport async function readSkillState(opts: ReadSkillStateOptions): Promise<SkillState> {\n const empty: SkillState = {installedAgentDisplayNames: new Set()}\n\n let stdout: string\n try {\n const result = await execa(process.execPath, [SKILLS_BIN_PATH, 'list', '-g', '--json'], {\n stdio: 'pipe',\n timeout: 10_000,\n })\n stdout = result.stdout\n } catch (error) {\n debug('skills list failed: %O', error)\n return empty\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (error) {\n debug('Failed to parse skills list JSON: %O', error)\n return empty\n }\n\n if (!Array.isArray(parsed)) {\n debug('Unexpected skills list JSON shape (not an array)')\n return empty\n }\n\n const agentSets =
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/skills/readSkillState.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {execa} from 'execa'\n\nimport {\n getSkillsCliAgentDisplayName,\n isUniversalSkillsCliAgentByEditorName,\n UNIVERSAL_SKILLS_DIR,\n} from '../mcp/editorConfigs.js'\nimport {type Editor} from '../mcp/types.js'\nimport {SKILLS_BIN_PATH} from './setupSkills.js'\n\nconst debug = subdebug('skills:state')\n\ninterface ReadSkillStateOptions {\n /**\n * Detected editors. Used to credit \"universal\" agents (those that read from\n * the shared `.agents/skills` directory) when the skills live there — see\n * note in `readSkillState`.\n */\n editors: Editor[]\n /** Names of the skills to look up (match the `name` field in `skills list --json`). */\n skillNames: string[]\n}\n\ninterface SkillState {\n /** Display names of agents that have all of these skills installed globally. */\n installedAgentDisplayNames: Set<string>\n}\n\ninterface SkillListEntry {\n agents?: unknown\n name?: unknown\n path?: unknown\n}\n\n/** Normalizes path separators to `/` so comparisons are OS-agnostic. */\nfunction toPosixPath(value: string): string {\n return value.replaceAll('\\\\', '/')\n}\n\n/**\n * A skill lives in the shared universal skills directory when its canonical\n * path is under `.agents/skills`. The `skills` CLI installs universal-agent\n * skills there directly (rather than under an agent-specific directory).\n */\nfunction isUniversalSkillPath(skillPath: unknown): boolean {\n if (typeof skillPath !== 'string') return false\n return toPosixPath(skillPath).includes(`${UNIVERSAL_SKILLS_DIR}/`)\n}\n\n/**\n * Runs the bundled `skills list -g --json` and returns the set of agent\n * display names that have every skill in `skillNames` installed globally.\n * Agents missing any of the skills are excluded, so they get a (idempotent)\n * re-install that fills the gap.\n *\n * \"Universal\" agents (those whose `skillsDir` is the shared `.agents/skills`\n * directory) are credited whenever every requested skill lives in that shared\n * directory. `skills list` only attributes a skill to agents it independently\n * detects, so a universal editor we detected may be absent from the output even\n * though it reads from the shared directory and already has the skills.\n *\n * Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and\n * resolved with an empty set. Callers should treat that as \"treat all agents\n * as not installed\" — re-installing is idempotent, so over-installing is\n * safer than skipping based on a flaky probe.\n */\nexport async function readSkillState(opts: ReadSkillStateOptions): Promise<SkillState> {\n const {editors, skillNames} = opts\n const empty: SkillState = {installedAgentDisplayNames: new Set()}\n\n let stdout: string\n try {\n const result = await execa(process.execPath, [SKILLS_BIN_PATH, 'list', '-g', '--json'], {\n stdio: 'pipe',\n timeout: 10_000,\n })\n stdout = result.stdout\n } catch (error) {\n debug('skills list failed: %O', error)\n return empty\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (error) {\n debug('Failed to parse skills list JSON: %O', error)\n return empty\n }\n\n if (!Array.isArray(parsed)) {\n debug('Unexpected skills list JSON shape (not an array)')\n return empty\n }\n\n const entries = parsed as SkillListEntry[]\n\n const agentSets = skillNames.map((skillName) => {\n const match = entries.find((entry) => entry?.name === skillName)\n if (!match || !Array.isArray(match.agents)) {\n return new Set<string>()\n }\n return new Set(match.agents.filter((a): a is string => typeof a === 'string'))\n })\n\n const installed = new Set(\n [...(agentSets[0] ?? [])].filter((agent) => agentSets.every((set) => set.has(agent))),\n )\n\n // Universal agents share the `.agents/skills` directory. If every requested\n // skill lives there, credit each detected universal editor — otherwise a\n // detected universal editor that the `skills` CLI didn't independently detect\n // would be treated as missing and trigger a redundant re-install.\n const allSkillsInUniversalDir =\n skillNames.length > 0 &&\n skillNames.every((skillName) => {\n const match = entries.find((entry) => entry?.name === skillName)\n return match ? isUniversalSkillPath(match.path) : false\n })\n\n if (allSkillsInUniversalDir) {\n for (const editor of editors) {\n if (!isUniversalSkillsCliAgentByEditorName(editor.name)) continue\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n if (displayName) installed.add(displayName)\n }\n }\n\n return {installedAgentDisplayNames: installed}\n}\n"],"names":["subdebug","execa","getSkillsCliAgentDisplayName","isUniversalSkillsCliAgentByEditorName","UNIVERSAL_SKILLS_DIR","SKILLS_BIN_PATH","debug","toPosixPath","value","replaceAll","isUniversalSkillPath","skillPath","includes","readSkillState","opts","editors","skillNames","empty","installedAgentDisplayNames","Set","stdout","result","process","execPath","stdio","timeout","error","parsed","JSON","parse","Array","isArray","entries","agentSets","map","skillName","match","find","entry","name","agents","filter","a","installed","agent","every","set","has","allSkillsInUniversalDir","length","path","editor","displayName","add"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,KAAK,QAAO,QAAO;AAE3B,SACEC,4BAA4B,EAC5BC,qCAAqC,EACrCC,oBAAoB,QACf,0BAAyB;AAEhC,SAAQC,eAAe,QAAO,mBAAkB;AAEhD,MAAMC,QAAQN,SAAS;AAwBvB,sEAAsE,GACtE,SAASO,YAAYC,KAAa;IAChC,OAAOA,MAAMC,UAAU,CAAC,MAAM;AAChC;AAEA;;;;CAIC,GACD,SAASC,qBAAqBC,SAAkB;IAC9C,IAAI,OAAOA,cAAc,UAAU,OAAO;IAC1C,OAAOJ,YAAYI,WAAWC,QAAQ,CAAC,GAAGR,qBAAqB,CAAC,CAAC;AACnE;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,eAAeS,eAAeC,IAA2B;IAC9D,MAAM,EAACC,OAAO,EAAEC,UAAU,EAAC,GAAGF;IAC9B,MAAMG,QAAoB;QAACC,4BAA4B,IAAIC;IAAK;IAEhE,IAAIC;IACJ,IAAI;QACF,MAAMC,SAAS,MAAMpB,MAAMqB,QAAQC,QAAQ,EAAE;YAAClB;YAAiB;YAAQ;YAAM;SAAS,EAAE;YACtFmB,OAAO;YACPC,SAAS;QACX;QACAL,SAASC,OAAOD,MAAM;IACxB,EAAE,OAAOM,OAAO;QACdpB,MAAM,0BAA0BoB;QAChC,OAAOT;IACT;IAEA,IAAIU;IACJ,IAAI;QACFA,SAASC,KAAKC,KAAK,CAACT;IACtB,EAAE,OAAOM,OAAO;QACdpB,MAAM,wCAAwCoB;QAC9C,OAAOT;IACT;IAEA,IAAI,CAACa,MAAMC,OAAO,CAACJ,SAAS;QAC1BrB,MAAM;QACN,OAAOW;IACT;IAEA,MAAMe,UAAUL;IAEhB,MAAMM,YAAYjB,WAAWkB,GAAG,CAAC,CAACC;QAChC,MAAMC,QAAQJ,QAAQK,IAAI,CAAC,CAACC,QAAUA,OAAOC,SAASJ;QACtD,IAAI,CAACC,SAAS,CAACN,MAAMC,OAAO,CAACK,MAAMI,MAAM,GAAG;YAC1C,OAAO,IAAIrB;QACb;QACA,OAAO,IAAIA,IAAIiB,MAAMI,MAAM,CAACC,MAAM,CAAC,CAACC,IAAmB,OAAOA,MAAM;IACtE;IAEA,MAAMC,YAAY,IAAIxB,IACpB;WAAKc,SAAS,CAAC,EAAE,IAAI,EAAE;KAAE,CAACQ,MAAM,CAAC,CAACG,QAAUX,UAAUY,KAAK,CAAC,CAACC,MAAQA,IAAIC,GAAG,CAACH;IAG/E,4EAA4E;IAC5E,yEAAyE;IACzE,8EAA8E;IAC9E,kEAAkE;IAClE,MAAMI,0BACJhC,WAAWiC,MAAM,GAAG,KACpBjC,WAAW6B,KAAK,CAAC,CAACV;QAChB,MAAMC,QAAQJ,QAAQK,IAAI,CAAC,CAACC,QAAUA,OAAOC,SAASJ;QACtD,OAAOC,QAAQ1B,qBAAqB0B,MAAMc,IAAI,IAAI;IACpD;IAEF,IAAIF,yBAAyB;QAC3B,KAAK,MAAMG,UAAUpC,QAAS;YAC5B,IAAI,CAACZ,sCAAsCgD,OAAOZ,IAAI,GAAG;YACzD,MAAMa,cAAclD,6BAA6BiD,OAAOZ,IAAI;YAC5D,IAAIa,aAAaT,UAAUU,GAAG,CAACD;QACjC;IACF;IAEA,OAAO;QAAClC,4BAA4ByB;IAAS;AAC/C"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import {
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
3
|
import { subdebug } from '@sanity/cli-core';
|
|
4
|
-
import { logSymbols } from '@sanity/cli-core/ux';
|
|
4
|
+
import { logSymbols, spinner } from '@sanity/cli-core/ux';
|
|
5
5
|
import { execa } from 'execa';
|
|
6
6
|
import { getErrorMessage, toError } from '../../util/getErrorMessage.js';
|
|
7
|
+
import { getSkillsCliAgentDisplayNameById, getSkillsCliAgentSkillsDir, UNIVERSAL_SKILLS_DIR } from '../mcp/editorConfigs.js';
|
|
7
8
|
const skillsDebug = subdebug('skills:setup');
|
|
8
9
|
/** Source repo for the bundled `skills` CLI. See https://www.sanity.io/docs/ai/skills. */ export const SANITY_SKILLS_REPO = 'sanity-io/agent-toolkit';
|
|
9
10
|
/** Names of the skills we install — must match the entries in the source repo. */ export const SANITY_SKILL_NAMES = [
|
|
@@ -15,11 +16,47 @@ const skillsDebug = subdebug('skills:setup');
|
|
|
15
16
|
* via `import.meta.resolve` so we run the version pinned in our package.json
|
|
16
17
|
* instead of paying the `npx -y` registry lookup at runtime.
|
|
17
18
|
*/ export const SKILLS_BIN_PATH = fileURLToPath(import.meta.resolve('skills/bin/cli.mjs', import.meta.url));
|
|
19
|
+
/**
|
|
20
|
+
* Prints a short summary beneath the success line: the skills that were
|
|
21
|
+
* installed, grouped by where they live. Universal agents share
|
|
22
|
+
* `~/.agents/skills`, so they're listed together under one header; any agent
|
|
23
|
+
* with its own directory (e.g. Claude Code) is listed separately with its
|
|
24
|
+
* location. Global (`-g`) installs are home-anchored, so locations show `~/`.
|
|
25
|
+
*/ function printInstallSummary(agents, output) {
|
|
26
|
+
const universal = [];
|
|
27
|
+
const additional = [];
|
|
28
|
+
for (const agent of agents){
|
|
29
|
+
const name = getSkillsCliAgentDisplayNameById(agent) ?? agent;
|
|
30
|
+
const dir = getSkillsCliAgentSkillsDir(agent);
|
|
31
|
+
if (dir && dir !== UNIVERSAL_SKILLS_DIR) {
|
|
32
|
+
additional.push({
|
|
33
|
+
dir,
|
|
34
|
+
name
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
universal.push(name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (universal.length > 0) {
|
|
41
|
+
output.log('');
|
|
42
|
+
output.log(styleText('dim', ` Universal (~/${UNIVERSAL_SKILLS_DIR})`));
|
|
43
|
+
output.log(styleText('dim', ` ${universal.join(', ')}`));
|
|
44
|
+
}
|
|
45
|
+
if (additional.length > 0) {
|
|
46
|
+
output.log('');
|
|
47
|
+
output.log(styleText('dim', ' Additional agents'));
|
|
48
|
+
for (const { dir, name } of additional){
|
|
49
|
+
output.log(styleText('dim', ` ${name} (~/${dir})`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
output.log('');
|
|
53
|
+
}
|
|
18
54
|
/**
|
|
19
55
|
* Runs the bundled `skills add` globally for the given agents. Failures are
|
|
20
56
|
* surfaced as warnings and never throw — skills install is best-effort and
|
|
21
57
|
* must not abort `sanity init`.
|
|
22
58
|
*/ export async function setupSkills(options) {
|
|
59
|
+
const { output } = options;
|
|
23
60
|
const uniqueAgents = [
|
|
24
61
|
...new Set(options.agents)
|
|
25
62
|
];
|
|
@@ -44,6 +81,7 @@ const skillsDebug = subdebug('skills:setup');
|
|
|
44
81
|
'-y'
|
|
45
82
|
];
|
|
46
83
|
skillsDebug('Running: %s %s', process.execPath, args.join(' '));
|
|
84
|
+
const spin = spinner('Installing Sanity agent skills').start();
|
|
47
85
|
try {
|
|
48
86
|
const result = await execa(process.execPath, args, {
|
|
49
87
|
stdio: 'pipe',
|
|
@@ -51,7 +89,9 @@ const skillsDebug = subdebug('skills:setup');
|
|
|
51
89
|
});
|
|
52
90
|
skillsDebug('skills stdout: %s', result.stdout);
|
|
53
91
|
skillsDebug('skills stderr: %s', result.stderr);
|
|
54
|
-
|
|
92
|
+
spin.stop();
|
|
93
|
+
output.log(`${logSymbols.success} Sanity agent skills installed: [${SANITY_SKILL_NAMES.join(', ')}]`);
|
|
94
|
+
printInstallSummary(uniqueAgents, output);
|
|
55
95
|
return {
|
|
56
96
|
installedAgents: uniqueAgents,
|
|
57
97
|
skipped: false
|
|
@@ -59,11 +99,12 @@ const skillsDebug = subdebug('skills:setup');
|
|
|
59
99
|
} catch (error) {
|
|
60
100
|
skillsDebug('Error installing skills %O', error);
|
|
61
101
|
const err = toError(error);
|
|
62
|
-
|
|
102
|
+
spin.stop();
|
|
103
|
+
output.warn(`Could not install Sanity agent skills: ${getErrorMessage(error)}`);
|
|
63
104
|
if (error && typeof error === 'object') {
|
|
64
105
|
const { stderr, stdout } = error;
|
|
65
|
-
if (stdout)
|
|
66
|
-
if (stderr)
|
|
106
|
+
if (stdout) output.warn(stdout);
|
|
107
|
+
if (stderr) output.warn(stderr);
|
|
67
108
|
}
|
|
68
109
|
return {
|
|
69
110
|
error: err,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/skills/setupSkills.ts"],"sourcesContent":["import {fileURLToPath} from 'node:url'\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/skills/setupSkills.ts"],"sourcesContent":["import {fileURLToPath} from 'node:url'\nimport {styleText} from 'node:util'\n\nimport {type Output, subdebug} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {execa} from 'execa'\n\nimport {getErrorMessage, toError} from '../../util/getErrorMessage.js'\nimport {\n getSkillsCliAgentDisplayNameById,\n getSkillsCliAgentSkillsDir,\n UNIVERSAL_SKILLS_DIR,\n} from '../mcp/editorConfigs.js'\n\nconst skillsDebug = subdebug('skills:setup')\n\n/** Source repo for the bundled `skills` CLI. See https://www.sanity.io/docs/ai/skills. */\nexport const SANITY_SKILLS_REPO = 'sanity-io/agent-toolkit'\n\n/** Names of the skills we install — must match the entries in the source repo. */\nexport const SANITY_SKILL_NAMES = ['sanity-best-practices', 'sanity-migration']\n\n/**\n * Absolute path to the bundled `skills` CLI bin. Resolved once at module load\n * via `import.meta.resolve` so we run the version pinned in our package.json\n * instead of paying the `npx -y` registry lookup at runtime.\n */\nexport const SKILLS_BIN_PATH = fileURLToPath(\n import.meta.resolve('skills/bin/cli.mjs', import.meta.url),\n)\n\ninterface SetupSkillsOptions {\n /** Skills-CLI agent IDs (e.g. 'cursor', 'claude-code') to install for. */\n agents: string[]\n\n /**\n * Output to use for user-facing messages, so they go through the calling\n * command rather than directly to stdout/stderr.\n */\n output: Output\n}\n\ninterface SetupSkillsResult {\n /** Deduplicated `--agent` values passed to `skills add`. */\n installedAgents: string[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Prints a short summary beneath the success line: the skills that were\n * installed, grouped by where they live. Universal agents share\n * `~/.agents/skills`, so they're listed together under one header; any agent\n * with its own directory (e.g. Claude Code) is listed separately with its\n * location. Global (`-g`) installs are home-anchored, so locations show `~/`.\n */\nfunction printInstallSummary(agents: string[], output: Output): void {\n const universal: string[] = []\n const additional: {dir: string; name: string}[] = []\n\n for (const agent of agents) {\n const name = getSkillsCliAgentDisplayNameById(agent) ?? agent\n const dir = getSkillsCliAgentSkillsDir(agent)\n if (dir && dir !== UNIVERSAL_SKILLS_DIR) {\n additional.push({dir, name})\n } else {\n universal.push(name)\n }\n }\n\n if (universal.length > 0) {\n output.log('')\n output.log(styleText('dim', ` Universal (~/${UNIVERSAL_SKILLS_DIR})`))\n output.log(styleText('dim', ` ${universal.join(', ')}`))\n }\n\n if (additional.length > 0) {\n output.log('')\n output.log(styleText('dim', ' Additional agents'))\n for (const {dir, name} of additional) {\n output.log(styleText('dim', ` ${name} (~/${dir})`))\n }\n }\n output.log('')\n}\n\n/**\n * Runs the bundled `skills add` globally for the given agents. Failures are\n * surfaced as warnings and never throw — skills install is best-effort and\n * must not abort `sanity init`.\n */\nexport async function setupSkills(options: SetupSkillsOptions): Promise<SetupSkillsResult> {\n const {output} = options\n const uniqueAgents = [...new Set(options.agents)]\n\n if (uniqueAgents.length === 0) {\n skillsDebug('No agents passed — skipping skills install')\n return {installedAgents: [], skipped: true}\n }\n\n const args = [\n SKILLS_BIN_PATH,\n 'add',\n SANITY_SKILLS_REPO,\n '--skill',\n ...SANITY_SKILL_NAMES,\n '-g',\n ...uniqueAgents.flatMap((agent) => ['-a', agent]),\n '-y',\n ]\n\n skillsDebug('Running: %s %s', process.execPath, args.join(' '))\n\n const spin = spinner('Installing Sanity agent skills').start()\n\n try {\n const result = await execa(process.execPath, args, {stdio: 'pipe', timeout: 90_000})\n skillsDebug('skills stdout: %s', result.stdout)\n skillsDebug('skills stderr: %s', result.stderr)\n spin.stop()\n output.log(\n `${logSymbols.success} Sanity agent skills installed: [${SANITY_SKILL_NAMES.join(', ')}]`,\n )\n printInstallSummary(uniqueAgents, output)\n return {installedAgents: uniqueAgents, skipped: false}\n } catch (error) {\n skillsDebug('Error installing skills %O', error)\n const err = toError(error)\n spin.stop()\n output.warn(`Could not install Sanity agent skills: ${getErrorMessage(error)}`)\n if (error && typeof error === 'object') {\n const {stderr, stdout} = error as {stderr?: string; stdout?: string}\n if (stdout) output.warn(stdout)\n if (stderr) output.warn(stderr)\n }\n return {error: err, installedAgents: [], skipped: false}\n }\n}\n"],"names":["fileURLToPath","styleText","subdebug","logSymbols","spinner","execa","getErrorMessage","toError","getSkillsCliAgentDisplayNameById","getSkillsCliAgentSkillsDir","UNIVERSAL_SKILLS_DIR","skillsDebug","SANITY_SKILLS_REPO","SANITY_SKILL_NAMES","SKILLS_BIN_PATH","resolve","url","printInstallSummary","agents","output","universal","additional","agent","name","dir","push","length","log","join","setupSkills","options","uniqueAgents","Set","installedAgents","skipped","args","flatMap","process","execPath","spin","start","result","stdio","timeout","stdout","stderr","stop","success","error","err","warn"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getSkillsCliAgent, getSkillsCliAgentDisplayName } from '../mcp/editorConfigs.js';
|
|
2
|
+
import { readSkillState } from './readSkillState.js';
|
|
3
|
+
import { SANITY_SKILL_NAMES } from './setupSkills.js';
|
|
4
|
+
/**
|
|
5
|
+
* Probe the global skill state and return the set of skills-CLI agent display
|
|
6
|
+
* names that already have all Sanity skills installed.
|
|
7
|
+
*
|
|
8
|
+
* Best-effort: `readSkillState` swallows failures and returns an empty set, so
|
|
9
|
+
* callers treat agents as "not installed" rather than skipping on a flaky probe.
|
|
10
|
+
*/ export async function getInstalledSkillAgentDisplayNames(editors) {
|
|
11
|
+
const { installedAgentDisplayNames } = await readSkillState({
|
|
12
|
+
editors,
|
|
13
|
+
skillNames: SANITY_SKILL_NAMES
|
|
14
|
+
});
|
|
15
|
+
return installedAgentDisplayNames;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Single source of truth for "which detected editors still want a skills
|
|
19
|
+
* install". An editor is a candidate when it has a skills-CLI mapping AND the
|
|
20
|
+
* Sanity skills are not already installed for its agent.
|
|
21
|
+
*
|
|
22
|
+
* Used by both the standalone `skills install` flow (`configureSkills`) and
|
|
23
|
+
* the combined `init` flow (`setupMCP`).
|
|
24
|
+
*/ export async function getSkillCandidates(editors) {
|
|
25
|
+
const installedAgentDisplayNames = await getInstalledSkillAgentDisplayNames(editors);
|
|
26
|
+
const candidates = [];
|
|
27
|
+
for (const editor of editors){
|
|
28
|
+
const agent = getSkillsCliAgent(editor.name);
|
|
29
|
+
if (!agent) continue;
|
|
30
|
+
const displayName = getSkillsCliAgentDisplayName(editor.name);
|
|
31
|
+
if (displayName && installedAgentDisplayNames.has(displayName)) continue;
|
|
32
|
+
candidates.push({
|
|
33
|
+
agent,
|
|
34
|
+
editor
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return candidates;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=skillCandidates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/skills/skillCandidates.ts"],"sourcesContent":["import {getSkillsCliAgent, getSkillsCliAgentDisplayName} from '../mcp/editorConfigs.js'\nimport {type Editor} from '../mcp/types.js'\nimport {readSkillState} from './readSkillState.js'\nimport {SANITY_SKILL_NAMES} from './setupSkills.js'\n\nexport interface SkillCandidate {\n /** skills-CLI agent id to pass to `skills add -a`. */\n agent: string\n editor: Editor\n}\n\n/**\n * Probe the global skill state and return the set of skills-CLI agent display\n * names that already have all Sanity skills installed.\n *\n * Best-effort: `readSkillState` swallows failures and returns an empty set, so\n * callers treat agents as \"not installed\" rather than skipping on a flaky probe.\n */\nexport async function getInstalledSkillAgentDisplayNames(editors: Editor[]): Promise<Set<string>> {\n const {installedAgentDisplayNames} = await readSkillState({\n editors,\n skillNames: SANITY_SKILL_NAMES,\n })\n return installedAgentDisplayNames\n}\n\n/**\n * Single source of truth for \"which detected editors still want a skills\n * install\". An editor is a candidate when it has a skills-CLI mapping AND the\n * Sanity skills are not already installed for its agent.\n *\n * Used by both the standalone `skills install` flow (`configureSkills`) and\n * the combined `init` flow (`setupMCP`).\n */\nexport async function getSkillCandidates(editors: Editor[]): Promise<SkillCandidate[]> {\n const installedAgentDisplayNames = await getInstalledSkillAgentDisplayNames(editors)\n const candidates: SkillCandidate[] = []\n\n for (const editor of editors) {\n const agent = getSkillsCliAgent(editor.name)\n if (!agent) continue\n\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n if (displayName && installedAgentDisplayNames.has(displayName)) continue\n\n candidates.push({agent, editor})\n }\n\n return candidates\n}\n"],"names":["getSkillsCliAgent","getSkillsCliAgentDisplayName","readSkillState","SANITY_SKILL_NAMES","getInstalledSkillAgentDisplayNames","editors","installedAgentDisplayNames","skillNames","getSkillCandidates","candidates","editor","agent","name","displayName","has","push"],"mappings":"AAAA,SAAQA,iBAAiB,EAAEC,4BAA4B,QAAO,0BAAyB;AAEvF,SAAQC,cAAc,QAAO,sBAAqB;AAClD,SAAQC,kBAAkB,QAAO,mBAAkB;AAQnD;;;;;;CAMC,GACD,OAAO,eAAeC,mCAAmCC,OAAiB;IACxE,MAAM,EAACC,0BAA0B,EAAC,GAAG,MAAMJ,eAAe;QACxDG;QACAE,YAAYJ;IACd;IACA,OAAOG;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeE,mBAAmBH,OAAiB;IACxD,MAAMC,6BAA6B,MAAMF,mCAAmCC;IAC5E,MAAMI,aAA+B,EAAE;IAEvC,KAAK,MAAMC,UAAUL,QAAS;QAC5B,MAAMM,QAAQX,kBAAkBU,OAAOE,IAAI;QAC3C,IAAI,CAACD,OAAO;QAEZ,MAAME,cAAcZ,6BAA6BS,OAAOE,IAAI;QAC5D,IAAIC,eAAeP,2BAA2BQ,GAAG,CAACD,cAAc;QAEhEJ,WAAWM,IAAI,CAAC;YAACJ;YAAOD;QAAM;IAChC;IAEA,OAAOD;AACT"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
|
-
import { ux } from '@oclif/core';
|
|
3
2
|
import { getUserConfig, isCi } from '@sanity/cli-core';
|
|
4
3
|
import { boxen } from '@sanity/cli-core/ux';
|
|
5
4
|
import { telemetryDebug } from './telemetryDebug.js';
|
|
6
5
|
import { telemetryLearnMoreMessage } from './telemetryLearnMoreMessage.js';
|
|
7
6
|
const TELEMETRY_DISCLOSED_CONFIG_KEY = 'telemetryDisclosed';
|
|
8
|
-
export function telemetryDisclosure() {
|
|
7
|
+
export function telemetryDisclosure({ logToStderr }) {
|
|
9
8
|
const userConfig = getUserConfig();
|
|
10
9
|
if (isCi()) {
|
|
11
10
|
telemetryDebug('CI environment detected, skipping telemetry disclosure');
|
|
@@ -16,7 +15,7 @@ export function telemetryDisclosure() {
|
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
18
17
|
// Print to stderr to prevent garbling command output
|
|
19
|
-
|
|
18
|
+
logToStderr(boxen(`The Sanity CLI now collects telemetry data on general usage and errors.
|
|
20
19
|
This helps us improve Sanity and prioritize features.
|
|
21
20
|
|
|
22
21
|
To opt in/out, run ${styleText('cyan', 'npx sanity telemetry enable/disable')}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/telemetry/telemetryDisclosure.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/telemetry/telemetryDisclosure.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {getUserConfig, isCi} from '@sanity/cli-core'\nimport {boxen} from '@sanity/cli-core/ux'\n\nimport {telemetryDebug} from './telemetryDebug.js'\nimport {telemetryLearnMoreMessage} from './telemetryLearnMoreMessage.js'\n\nconst TELEMETRY_DISCLOSED_CONFIG_KEY = 'telemetryDisclosed'\n\ninterface TelemetryDisclosureOptions {\n /**\n * Writes a plain (unprefixed) line to stderr. Injected by the caller so the\n * disclosure goes through the calling hook/command rather than directly to\n * the process streams.\n */\n logToStderr: (message: string) => void\n}\n\nexport function telemetryDisclosure({logToStderr}: TelemetryDisclosureOptions): void {\n const userConfig = getUserConfig()\n\n if (isCi()) {\n telemetryDebug('CI environment detected, skipping telemetry disclosure')\n return\n }\n\n if (userConfig.get(TELEMETRY_DISCLOSED_CONFIG_KEY)) {\n telemetryDebug('Telemetry disclosure has already been shown')\n return\n }\n\n // Print to stderr to prevent garbling command output\n logToStderr(\n boxen(\n `The Sanity CLI now collects telemetry data on general usage and errors.\nThis helps us improve Sanity and prioritize features.\n\nTo opt in/out, run ${styleText('cyan', 'npx sanity telemetry enable/disable')}.\n\n${telemetryLearnMoreMessage('unset')}`,\n {\n borderColor: 'yellow',\n borderStyle: 'round',\n margin: 1,\n padding: 1,\n },\n ),\n )\n\n userConfig.set(TELEMETRY_DISCLOSED_CONFIG_KEY, Date.now())\n}\n"],"names":["styleText","getUserConfig","isCi","boxen","telemetryDebug","telemetryLearnMoreMessage","TELEMETRY_DISCLOSED_CONFIG_KEY","telemetryDisclosure","logToStderr","userConfig","get","borderColor","borderStyle","margin","padding","set","Date","now"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,aAAa,EAAEC,IAAI,QAAO,mBAAkB;AACpD,SAAQC,KAAK,QAAO,sBAAqB;AAEzC,SAAQC,cAAc,QAAO,sBAAqB;AAClD,SAAQC,yBAAyB,QAAO,iCAAgC;AAExE,MAAMC,iCAAiC;AAWvC,OAAO,SAASC,oBAAoB,EAACC,WAAW,EAA6B;IAC3E,MAAMC,aAAaR;IAEnB,IAAIC,QAAQ;QACVE,eAAe;QACf;IACF;IAEA,IAAIK,WAAWC,GAAG,CAACJ,iCAAiC;QAClDF,eAAe;QACf;IACF;IAEA,qDAAqD;IACrDI,YACEL,MACE,CAAC;;;mBAGY,EAAEH,UAAU,QAAQ,uCAAuC;;AAE9E,EAAEK,0BAA0B,UAAU,EAChC;QACEM,aAAa;QACbC,aAAa;QACbC,QAAQ;QACRC,SAAS;IACX;IAIJL,WAAWM,GAAG,CAACT,gCAAgCU,KAAKC,GAAG;AACzD"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { ux } from '@oclif/core';
|
|
2
1
|
import { getTokenRoles } from '../../services/tokens.js';
|
|
3
2
|
/**
|
|
4
3
|
* Validate a role name
|
|
5
4
|
* @param roleName - The role name to validate
|
|
6
5
|
* @param projectId - The project ID
|
|
6
|
+
* @param output - Output to raise the error through the calling command
|
|
7
7
|
* @returns A promise that resolves to the validated role name
|
|
8
8
|
*
|
|
9
9
|
* @internal
|
|
10
|
-
*/ export async function validateRole(roleName, projectId) {
|
|
10
|
+
*/ export async function validateRole(roleName, projectId, output) {
|
|
11
11
|
const roles = await getTokenRoles(projectId);
|
|
12
12
|
const robotRoles = roles.filter((role)=>role.appliesToRobots);
|
|
13
13
|
const role = robotRoles.find((r)=>r.name === roleName);
|
|
@@ -15,7 +15,7 @@ import { getTokenRoles } from '../../services/tokens.js';
|
|
|
15
15
|
return roleName;
|
|
16
16
|
}
|
|
17
17
|
const availableRoles = robotRoles.map((r)=>r.name).join(', ');
|
|
18
|
-
|
|
18
|
+
return output.error(`Invalid role "${roleName}". Available roles: ${availableRoles}`, {
|
|
19
19
|
exit: 1
|
|
20
20
|
});
|
|
21
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/tokens/validateRole.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/tokens/validateRole.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\n\nimport {getTokenRoles} from '../../services/tokens.js'\n\n/**\n * Validate a role name\n * @param roleName - The role name to validate\n * @param projectId - The project ID\n * @param output - Output to raise the error through the calling command\n * @returns A promise that resolves to the validated role name\n *\n * @internal\n */\nexport async function validateRole(\n roleName: string,\n projectId: string,\n output: Output,\n): Promise<string> {\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n const role = robotRoles.find((r) => r.name === roleName)\n if (role) {\n return roleName\n }\n\n const availableRoles = robotRoles.map((r) => r.name).join(', ')\n return output.error(`Invalid role \"${roleName}\". Available roles: ${availableRoles}`, {exit: 1})\n}\n"],"names":["getTokenRoles","validateRole","roleName","projectId","output","roles","robotRoles","filter","role","appliesToRobots","find","r","name","availableRoles","map","join","error","exit"],"mappings":"AAEA,SAAQA,aAAa,QAAO,2BAA0B;AAEtD;;;;;;;;CAQC,GACD,OAAO,eAAeC,aACpBC,QAAgB,EAChBC,SAAiB,EACjBC,MAAc;IAEd,MAAMC,QAAQ,MAAML,cAAcG;IAClC,MAAMG,aAAaD,MAAME,MAAM,CAAC,CAACC,OAASA,KAAKC,eAAe;IAE9D,MAAMD,OAAOF,WAAWI,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKV;IAC/C,IAAIM,MAAM;QACR,OAAON;IACT;IAEA,MAAMW,iBAAiBP,WAAWQ,GAAG,CAAC,CAACH,IAAMA,EAAEC,IAAI,EAAEG,IAAI,CAAC;IAC1D,OAAOX,OAAOY,KAAK,CAAC,CAAC,cAAc,EAAEd,SAAS,oBAAoB,EAAEW,gBAAgB,EAAE;QAACI,MAAM;IAAC;AAChG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/disable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst disableBackupDebug = subdebug('backup:disable')\n\nexport class DisableBackupCommand extends SanityCommand<typeof DisableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to disable backup for',\n required: false,\n }),\n }\n\n static override description = 'Disable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively disable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Disable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to disable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:disable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DisableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n try {\n await setBackup({dataset, projectId, status: false})\n\n this.log(`${styleText('green', `Disabled daily backups for dataset ${dataset}.\\n`)}`)\n this.log(\n `${styleText('yellow', 'Note: Existing backups will be retained according to your retention policy.\\n')}`,\n )\n\n disableBackupDebug(`Successfully disabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to disable backup for dataset`, error)\n this.error(`Disabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n const err = error as Error\n disableBackupDebug(`Error fetching datasets`, err)\n this.error(`Failed to fetch datasets:\\n${err.message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","setBackup","listDatasets","getProjectIdFlag","disableBackupDebug","DisableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","promptForDataset","status","log","choices","map","name","value","err"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,QAAO,cAAa;AAChC,SAAQC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,MAAM,QAAO,sBAAqB;AAG1C,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,qBAAqBP,SAAS;AAEpC,OAAO,MAAMQ,6BAA6BT;IACxC,OAAgBU,OAAO;QACrBC,SAASZ,KAAKa,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,+BAA8B;IAE5D,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGV,iBAAiB;YAClBM,aAAa;YACbK,WAAW;QACb,EAAE;IACJ,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAiB,CAAA;IAE5D,MAAaC,MAAqB;QAChC,MAAM,EAACV,IAAI,EAAC,GAAG,MAAM,IAAI,CAACW,KAAK,CAACZ;QAChC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,MAAMY,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRpB,iBAAiB;oBACfqB,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;wBACrD;4BAACD,OAAO;4BAAUC,YAAY;wBAAyB;qBACxD;gBACH;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAMtB,aAAagB;QAChC,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChErB,mBAAmB,CAAC,yBAAyB,EAAEsB,SAAS,EAAED;YAC1D,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEC,SAAS,EAAE;gBAACG,MAAM;YAAC;QAC5D;QAEA,IAAIL,SAASM,MAAM,KAAK,GAAG;YACzB,IAAI,CAACL,KAAK,CAAC,sCAAsC;gBAACI,MAAM;YAAC;QAC3D;QAEA,IAAItB,SAAS;YACXR,oBAAoByB,UAAUjB;
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/disable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst disableBackupDebug = subdebug('backup:disable')\n\nexport class DisableBackupCommand extends SanityCommand<typeof DisableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to disable backup for',\n required: false,\n }),\n }\n\n static override description = 'Disable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively disable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Disable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to disable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:disable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DisableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n try {\n await setBackup({dataset, projectId, status: false})\n\n this.log(`${styleText('green', `Disabled daily backups for dataset ${dataset}.\\n`)}`)\n this.log(\n `${styleText('yellow', 'Note: Existing backups will be retained according to your retention policy.\\n')}`,\n )\n\n disableBackupDebug(`Successfully disabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to disable backup for dataset`, error)\n this.error(`Disabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n const err = error as Error\n disableBackupDebug(`Error fetching datasets`, err)\n this.error(`Failed to fetch datasets:\\n${err.message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","setBackup","listDatasets","getProjectIdFlag","disableBackupDebug","DisableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","output","promptForDataset","status","log","choices","map","name","value","err"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,QAAO,cAAa;AAChC,SAAQC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,MAAM,QAAO,sBAAqB;AAG1C,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,qBAAqBP,SAAS;AAEpC,OAAO,MAAMQ,6BAA6BT;IACxC,OAAgBU,OAAO;QACrBC,SAASZ,KAAKa,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,+BAA8B;IAE5D,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGV,iBAAiB;YAClBM,aAAa;YACbK,WAAW;QACb,EAAE;IACJ,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAiB,CAAA;IAE5D,MAAaC,MAAqB;QAChC,MAAM,EAACV,IAAI,EAAC,GAAG,MAAM,IAAI,CAACW,KAAK,CAACZ;QAChC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,MAAMY,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRpB,iBAAiB;oBACfqB,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;wBACrD;4BAACD,OAAO;4BAAUC,YAAY;wBAAyB;qBACxD;gBACH;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAMtB,aAAagB;QAChC,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChErB,mBAAmB,CAAC,yBAAyB,EAAEsB,SAAS,EAAED;YAC1D,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEC,SAAS,EAAE;gBAACG,MAAM;YAAC;QAC5D;QAEA,IAAIL,SAASM,MAAM,KAAK,GAAG;YACzB,IAAI,CAACL,KAAK,CAAC,sCAAsC;gBAACI,MAAM;YAAC;QAC3D;QAEA,IAAItB,SAAS;YACXR,oBAAoByB,UAAUjB,SAAS,IAAI,CAACwB,MAAM;QACpD,OAAO;YACLxB,UAAU,MAAM,IAAI,CAACyB,gBAAgB,CAACR;QACxC;QAEA,IAAI;YACF,MAAMvB,UAAU;gBAACM;gBAASW;gBAAWe,QAAQ;YAAK;YAElD,IAAI,CAACC,GAAG,CAAC,GAAGxC,UAAU,SAAS,CAAC,mCAAmC,EAAEa,QAAQ,GAAG,CAAC,GAAG;YACpF,IAAI,CAAC2B,GAAG,CACN,GAAGxC,UAAU,UAAU,kFAAkF;YAG3GU,mBAAmB,CAAC,yCAAyC,EAAEG,SAAS;QAC1E,EAAE,OAAOkB,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChErB,mBAAmB,CAAC,oCAAoC,CAAC,EAAEqB;YAC3D,IAAI,CAACA,KAAK,CAAC,CAAC,iCAAiC,EAAEC,SAAS,EAAE;gBAACG,MAAM;YAAC;QACpE;IACF;IAEA,MAAcG,iBAAiBR,QAA0B,EAAmB;QAC1E,IAAI;YACF,MAAMW,UAAUX,SAASY,GAAG,CAAC,CAAC7B,UAAa,CAAA;oBACzC8B,MAAM9B,QAAQ8B,IAAI;oBAClBC,OAAO/B,QAAQ8B,IAAI;gBACrB,CAAA;YAEA,OAAOvC,OAAO;gBACZqC;gBACAT,SAAS;YACX;QACF,EAAE,OAAOD,OAAO;YACd,MAAMc,MAAMd;YACZrB,mBAAmB,CAAC,uBAAuB,CAAC,EAAEmC;YAC9C,IAAI,CAACd,KAAK,CAAC,CAAC,2BAA2B,EAAEc,IAAIb,OAAO,EAAE,EAAE;gBAACG,MAAM;YAAC;QAClE;IACF;AACF"}
|
|
@@ -105,7 +105,7 @@ export class DownloadBackupCommand extends SanityCommand {
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
if (dataset) {
|
|
108
|
-
assertDatasetExists(datasets, dataset);
|
|
108
|
+
assertDatasetExists(datasets, dataset, this.output);
|
|
109
109
|
} else {
|
|
110
110
|
dataset = await promptForDataset({
|
|
111
111
|
allowCreation: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: 1})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: 1})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const inputResult = await input({\n default: path.join(workDir, defaultOutFileName),\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: 1})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`concurrency should be in 1 to ${MAX_DOWNLOAD_CONCURRENCY} range`, {exit: 1})\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: 1})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: 1})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","push","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","inputResult","promptForBackupId","exists","then","shouldOverwrite","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: 1})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: 1})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const inputResult = await input({\n default: path.join(workDir, defaultOutFileName),\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: 1})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`concurrency should be in 1 to ${MAX_DOWNLOAD_CONCURRENCY} range`, {exit: 1})\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: 1})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: 1})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","output","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","push","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","inputResult","promptForBackupId","exists","then","shouldOverwrite","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/enable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {NEW_DATASET_VALUE, promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {createDataset, listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst enableBackupDebug = subdebug('backup:enable')\n\nexport class EnableBackupCommand extends SanityCommand<typeof EnableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to enable backup for',\n required: false,\n }),\n }\n\n static override description = 'Enable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively enable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Enable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to enable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:enable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(EnableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n const hasProduction = datasets.some((dataset) => dataset.name === 'production')\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await promptForDataset({allowCreation: true, datasets})\n\n if (dataset === NEW_DATASET_VALUE) {\n const newDatasetName = await promptForDatasetName({\n default: hasProduction ? undefined : 'production',\n })\n\n try {\n await createDataset({\n datasetName: newDatasetName,\n projectId,\n })\n dataset = newDatasetName\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error)\n this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {exit: 1})\n }\n }\n }\n\n try {\n await setBackup({dataset, projectId, status: true})\n\n this.log(\n `${styleText(\n 'green',\n `Enabled backups for dataset ${dataset}.\\nPlease note that it may take up to 24 hours before the first backup is created.\\n`,\n )}`,\n )\n\n this.log(\n `${styleText('bold', `Retention policies may apply depending on your plan and agreement.\\n`)}`,\n )\n\n enableBackupDebug(`Successfully enabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to enable backup for dataset`, error)\n this.error(`Enabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","assertDatasetExists","NEW_DATASET_VALUE","promptForDataset","promptForDatasetName","promptForProject","setBackup","createDataset","listDatasets","getProjectIdFlag","enableBackupDebug","EnableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","hasProduction","some","name","length","allowCreation","newDatasetName","default","undefined","datasetName","status","log"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,QAAO,cAAa;AAChC,SAAQC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAGxD,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,iBAAiB,EAAEC,gBAAgB,QAAO,oCAAmC;AACrF,SAAQC,oBAAoB,QAAO,wCAAuC;AAC1E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,aAAa,EAAEC,YAAY,QAAO,6BAA4B;AACtE,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,oBAAoBV,SAAS;AAEnC,OAAO,MAAMW,4BAA4BZ;IACvC,OAAgBa,OAAO;QACrBC,SAASf,KAAKgB,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,8BAA6B;IAE3D,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGV,iBAAiB;YAClBM,aAAa;YACbK,WAAW;QACb,EAAE;IACJ,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAgB,CAAA;IAE3D,MAAaC,MAAqB;QAChC,MAAM,EAACV,IAAI,EAAC,GAAG,MAAM,IAAI,CAACW,KAAK,CAACZ;QAChC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,MAAMY,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRrB,iBAAiB;oBACfsB,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;wBACrD;4BAACD,OAAO;4BAAUC,YAAY;wBAAyB;qBACxD;gBACH;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAMtB,aAAagB;QAChC,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChErB,kBAAkB,CAAC,yBAAyB,EAAEsB,SAAS,EAAED;YACzD,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEC,SAAS,EAAE;gBAACG,MAAM;YAAC;QAC5D;QAEA,MAAMC,gBAAgBN,SAASO,IAAI,CAAC,CAACxB,UAAYA,QAAQyB,IAAI,KAAK;QAElE,IAAIR,SAASS,MAAM,KAAK,GAAG;YACzB,IAAI,CAACR,KAAK,CAAC,sCAAsC;gBAACI,MAAM;YAAC;QAC3D;QAEA,IAAItB,SAAS;YACXZ,oBAAoB6B,UAAUjB;
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/enable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {NEW_DATASET_VALUE, promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {createDataset, listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst enableBackupDebug = subdebug('backup:enable')\n\nexport class EnableBackupCommand extends SanityCommand<typeof EnableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to enable backup for',\n required: false,\n }),\n }\n\n static override description = 'Enable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively enable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Enable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to enable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:enable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(EnableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n const hasProduction = datasets.some((dataset) => dataset.name === 'production')\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: true, datasets})\n\n if (dataset === NEW_DATASET_VALUE) {\n const newDatasetName = await promptForDatasetName({\n default: hasProduction ? undefined : 'production',\n })\n\n try {\n await createDataset({\n datasetName: newDatasetName,\n projectId,\n })\n dataset = newDatasetName\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error)\n this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {exit: 1})\n }\n }\n }\n\n try {\n await setBackup({dataset, projectId, status: true})\n\n this.log(\n `${styleText(\n 'green',\n `Enabled backups for dataset ${dataset}.\\nPlease note that it may take up to 24 hours before the first backup is created.\\n`,\n )}`,\n )\n\n this.log(\n `${styleText('bold', `Retention policies may apply depending on your plan and agreement.\\n`)}`,\n )\n\n enableBackupDebug(`Successfully enabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to enable backup for dataset`, error)\n this.error(`Enabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","assertDatasetExists","NEW_DATASET_VALUE","promptForDataset","promptForDatasetName","promptForProject","setBackup","createDataset","listDatasets","getProjectIdFlag","enableBackupDebug","EnableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","hasProduction","some","name","length","output","allowCreation","newDatasetName","default","undefined","datasetName","status","log"],"mappings":"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"}
|