@gmickel/gno 1.0.5 → 1.1.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/README.md
CHANGED
|
@@ -420,7 +420,7 @@ gno search "incident review" --tags-all "status/active,team/platform"
|
|
|
420
420
|
# Export a publish artifact for gno.sh
|
|
421
421
|
gno publish export work-docs --out ~/Downloads/work-docs.json
|
|
422
422
|
gno publish export "gno://work-docs/runbooks/deploy.md" --out ~/Downloads/deploy.json
|
|
423
|
-
# Or let GNO choose
|
|
423
|
+
# Or let GNO choose your Downloads folder automatically
|
|
424
424
|
gno publish export work-docs
|
|
425
425
|
```
|
|
426
426
|
|
|
@@ -631,7 +631,7 @@ gno publish export "gno://work-docs/runbooks/deploy.md" \
|
|
|
631
631
|
--passphrase "correct horse battery staple" \
|
|
632
632
|
--out ~/Downloads/deploy-encrypted.json
|
|
633
633
|
|
|
634
|
-
# Let GNO pick the path
|
|
634
|
+
# Let GNO pick the path in your Downloads folder
|
|
635
635
|
gno publish export work-docs
|
|
636
636
|
```
|
|
637
637
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { getIndexDbPath } from "../../../app/constants";
|
|
6
|
-
import { loadConfig } from "../../../config";
|
|
6
|
+
import { ensureDirectories, loadConfig } from "../../../config";
|
|
7
7
|
import { resolveModelUri } from "../../../llm/registry";
|
|
8
8
|
import { SqliteAdapter } from "../../../store/sqlite/adapter";
|
|
9
9
|
import { CliError } from "../../errors";
|
|
@@ -33,6 +33,11 @@ export async function collectionClearEmbeddings(
|
|
|
33
33
|
throw new CliError("VALIDATION", `Collection not found: ${name}`);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const ensureResult = await ensureDirectories();
|
|
37
|
+
if (!ensureResult.ok) {
|
|
38
|
+
throw new CliError("RUNTIME", ensureResult.error.message);
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
const store = new SqliteAdapter();
|
|
37
42
|
const openResult = await store.open(getIndexDbPath(), config.ftsTokenizer);
|
|
38
43
|
if (!openResult.ok) {
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
8
|
import { dirname } from "node:path";
|
|
10
9
|
import { join } from "node:path";
|
|
11
10
|
|
|
@@ -15,6 +14,7 @@ import type {
|
|
|
15
14
|
} from "../../publish/artifact";
|
|
16
15
|
import type { SanitizeWarning } from "../../publish/obsidian-sanitize";
|
|
17
16
|
|
|
17
|
+
import { resolveDownloadsDir } from "../../core/user-dirs";
|
|
18
18
|
import { derivePublishArtifactFilename, slugify } from "../../publish/artifact";
|
|
19
19
|
import { exportPublishArtifact } from "../../publish/export-service";
|
|
20
20
|
import { formatSanitizeWarnings } from "../../publish/obsidian-sanitize";
|
|
@@ -50,16 +50,16 @@ function formatExportDateStamp(isoTimestamp: string): string {
|
|
|
50
50
|
return isoTimestamp.slice(0, 10).replaceAll("-", "");
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export function buildDefaultPublishExportPath(
|
|
53
|
+
export async function buildDefaultPublishExportPath(
|
|
54
54
|
artifact: PublishArtifact
|
|
55
|
-
): string {
|
|
55
|
+
): Promise<string> {
|
|
56
56
|
const fileName = derivePublishArtifactFilename(artifact).replace(
|
|
57
57
|
/\.json$/u,
|
|
58
58
|
""
|
|
59
59
|
);
|
|
60
|
+
const downloadsDir = await resolveDownloadsDir();
|
|
60
61
|
return join(
|
|
61
|
-
|
|
62
|
-
"Downloads",
|
|
62
|
+
downloadsDir,
|
|
63
63
|
`${fileName}-${formatExportDateStamp(artifact.exportedAt)}.json`
|
|
64
64
|
);
|
|
65
65
|
}
|
|
@@ -114,7 +114,7 @@ export async function publishExport(
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
const outPath =
|
|
117
|
-
options.out?.trim() || buildDefaultPublishExportPath(artifact);
|
|
117
|
+
options.out?.trim() || (await buildDefaultPublishExportPath(artifact));
|
|
118
118
|
|
|
119
119
|
await mkdir(dirname(outPath), { recursive: true });
|
|
120
120
|
await writeFile(outPath, JSON.stringify(artifact, null, 2));
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// node:os homedir: no Bun equivalent.
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
// node:path join/normalize: no Bun path utilities.
|
|
4
|
+
import { join, normalize } from "node:path";
|
|
5
|
+
|
|
6
|
+
interface ResolveDownloadsDirDeps {
|
|
7
|
+
env?: Record<string, string | undefined>;
|
|
8
|
+
homeDir?: string;
|
|
9
|
+
platform?: NodeJS.Platform;
|
|
10
|
+
readTextFile?: (path: string) => Promise<string | null>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const XDG_DOWNLOAD_DIR_REGEX = /^XDG_DOWNLOAD_DIR=(?:"([^"]+)"|([^\r\n#]+))$/mu;
|
|
14
|
+
|
|
15
|
+
function expandEnvPath(
|
|
16
|
+
value: string,
|
|
17
|
+
env: Record<string, string | undefined>,
|
|
18
|
+
homeDir: string
|
|
19
|
+
): string {
|
|
20
|
+
return normalize(
|
|
21
|
+
value
|
|
22
|
+
.trim()
|
|
23
|
+
.replaceAll(/\$HOME|\$\{HOME\}/gu, homeDir)
|
|
24
|
+
.replace(/^~(?=$|[\\/])/u, homeDir)
|
|
25
|
+
.replaceAll(
|
|
26
|
+
/%([^%]+)%/gu,
|
|
27
|
+
(_match, key: string) => env[key] ?? env[key.toUpperCase()] ?? ""
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseXdgDownloadsDir(
|
|
33
|
+
fileContents: string,
|
|
34
|
+
env: Record<string, string | undefined>,
|
|
35
|
+
homeDir: string
|
|
36
|
+
): string | null {
|
|
37
|
+
const match = fileContents.match(XDG_DOWNLOAD_DIR_REGEX);
|
|
38
|
+
const rawValue = match?.[1] ?? match?.[2];
|
|
39
|
+
if (!rawValue) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return expandEnvPath(rawValue, env, homeDir);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function defaultReadTextFile(path: string): Promise<string | null> {
|
|
46
|
+
const file = Bun.file(path);
|
|
47
|
+
if (!(await file.exists())) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return file.text();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function resolveDownloadsDir(
|
|
54
|
+
deps: ResolveDownloadsDirDeps = {}
|
|
55
|
+
): Promise<string> {
|
|
56
|
+
const env = deps.env ?? process.env;
|
|
57
|
+
const platform = deps.platform ?? process.platform;
|
|
58
|
+
const homeDir = deps.homeDir ?? homedir();
|
|
59
|
+
const readTextFile = deps.readTextFile ?? defaultReadTextFile;
|
|
60
|
+
|
|
61
|
+
if (platform === "linux") {
|
|
62
|
+
const explicit = env.XDG_DOWNLOAD_DIR?.trim();
|
|
63
|
+
if (explicit) {
|
|
64
|
+
return expandEnvPath(explicit, env, homeDir);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const xdgConfigHome =
|
|
68
|
+
env.XDG_CONFIG_HOME?.trim() || join(homeDir, ".config");
|
|
69
|
+
const userDirs = await readTextFile(join(xdgConfigHome, "user-dirs.dirs"));
|
|
70
|
+
if (userDirs) {
|
|
71
|
+
const parsed = parseXdgDownloadsDir(userDirs, env, homeDir);
|
|
72
|
+
if (parsed) {
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (platform === "win32") {
|
|
79
|
+
const userProfile = env.USERPROFILE?.trim();
|
|
80
|
+
if (userProfile) {
|
|
81
|
+
return join(userProfile, "Downloads");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return join(homeDir, "Downloads");
|
|
86
|
+
}
|