dsh-git-branch-manage 1.1.0 → 1.2.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.
@@ -1,37 +0,0 @@
1
- /**
2
- * Standalone git credential helper shipped with the dsh-git-branch plugin.
3
- *
4
- * The panel's credential file (`~/.dsh/git-credentials.json`) normally rides
5
- * `-c http.<url>.extraHeader` config on the routes and the `git_network` tool.
6
- * This helper extends that coverage to a plain shell `git push`: the plugin's
7
- * host process writes a session gitconfig (see `src/shell-bridge.ts`) that
8
- * installs this script as a URL-scoped credential helper for every stored
9
- * host, so commands the model runs in the shell authenticate as the
10
- * configured account too.
11
- *
12
- * Design constraints (see docs/decisions/0029, 0032 and 0034):
13
- * - The user's global git config and credential helpers are never written;
14
- * the injection lives in one generated session config plus the process
15
- * environment only.
16
- * - The token never enters the conversation: this script reads the
17
- * credential file directly on the host and answers git on stdout.
18
- * - URL scope (`credential.https://<host>/`) confines the helper to hosts
19
- * with a stored credential. The host half also clears the machine helper
20
- * list, so a host this script cannot answer fails fast instead of opening
21
- * an interactive credential dialog.
22
- * - `get` is silent on a miss (git falls through to the next helper);
23
- * `store`/`erase` are pass-through by decision — writing back would race
24
- * the panel's atomic save and leak the file shape.
25
- *
26
- * Environment contract (set by src/shell-bridge.ts). The names carry no
27
- * KEY/PASSWORD/SECRET/TOKEN substring and no `DSH_` prefix, because the DSH
28
- * subprocess scrubber strips exactly those from inherited environments
29
- * (decision 0034; a stripped path made this helper read the wrong file):
30
- * - `DGB_BRIDGE_HELPER` — credential file path (required).
31
- * - `DGB_BRIDGE_HOME` — user home directory (fallback resolution).
32
- * - `DGB_BRIDGE_DSH_HOME` — `$DSH_HOME` override (optional).
33
- * - `DGB_BRIDGE_DEBUG` — when non-blank, a debug log path (optional).
34
- *
35
- * @module dsh-git-branch/git-credential-bridge
36
- */
37
- export {};
@@ -1,119 +0,0 @@
1
- /**
2
- * Standalone git credential helper shipped with the dsh-git-branch plugin.
3
- *
4
- * The panel's credential file (`~/.dsh/git-credentials.json`) normally rides
5
- * `-c http.<url>.extraHeader` config on the routes and the `git_network` tool.
6
- * This helper extends that coverage to a plain shell `git push`: the plugin's
7
- * host process writes a session gitconfig (see `src/shell-bridge.ts`) that
8
- * installs this script as a URL-scoped credential helper for every stored
9
- * host, so commands the model runs in the shell authenticate as the
10
- * configured account too.
11
- *
12
- * Design constraints (see docs/decisions/0029, 0032 and 0034):
13
- * - The user's global git config and credential helpers are never written;
14
- * the injection lives in one generated session config plus the process
15
- * environment only.
16
- * - The token never enters the conversation: this script reads the
17
- * credential file directly on the host and answers git on stdout.
18
- * - URL scope (`credential.https://<host>/`) confines the helper to hosts
19
- * with a stored credential. The host half also clears the machine helper
20
- * list, so a host this script cannot answer fails fast instead of opening
21
- * an interactive credential dialog.
22
- * - `get` is silent on a miss (git falls through to the next helper);
23
- * `store`/`erase` are pass-through by decision — writing back would race
24
- * the panel's atomic save and leak the file shape.
25
- *
26
- * Environment contract (set by src/shell-bridge.ts). The names carry no
27
- * KEY/PASSWORD/SECRET/TOKEN substring and no `DSH_` prefix, because the DSH
28
- * subprocess scrubber strips exactly those from inherited environments
29
- * (decision 0034; a stripped path made this helper read the wrong file):
30
- * - `DGB_BRIDGE_HELPER` — credential file path (required).
31
- * - `DGB_BRIDGE_HOME` — user home directory (fallback resolution).
32
- * - `DGB_BRIDGE_DSH_HOME` — `$DSH_HOME` override (optional).
33
- * - `DGB_BRIDGE_DEBUG` — when non-blank, a debug log path (optional).
34
- *
35
- * @module dsh-git-branch/git-credential-bridge
36
- */
37
- import { appendFileSync, readFileSync } from 'node:fs';
38
- import { homedir } from 'node:os';
39
- import { join } from 'node:path';
40
- /** git helpers receive exactly one argument: the operation. */
41
- const OPERATION = process.argv[2] ?? 'get';
42
- /** The credential file this helper reads (`DGB_BRIDGE_HELPER` wins). */
43
- const DEFAULT_CREDENTIAL_FILE = 'git-credentials.json';
44
- /** The dsh home directory below the user profile when `$DSH_HOME` is unset. */
45
- const PACKAGE_HOME = '.dsh';
46
- /** The Basic-auth name a token without a username is sent under. */
47
- const TOKEN_USERNAME = 'oauth2';
48
- /**
49
- * Append one debug line behind the user's back; a broken log path must never
50
- * break the credential lookup.
51
- */
52
- function debug(line) {
53
- const path = process.env.DGB_BRIDGE_DEBUG;
54
- if (path === undefined || path.trim() === '')
55
- return;
56
- try {
57
- appendFileSync(path, `${line}\n`, 'utf8');
58
- }
59
- catch {
60
- // Deliberately swallowed: debugging is best-effort.
61
- }
62
- }
63
- /** The credential file this helper reads (`DGB_BRIDGE_HELPER` wins). */
64
- function credentialFile() {
65
- const helper = process.env.DGB_BRIDGE_HELPER;
66
- if (helper !== undefined && helper.trim() !== '')
67
- return helper;
68
- const dshHome = process.env.DGB_BRIDGE_DSH_HOME;
69
- const home = dshHome !== undefined && dshHome.trim() !== ''
70
- ? dshHome
71
- : process.env.DGB_BRIDGE_HOME ?? homedir();
72
- return join(home, PACKAGE_HOME, DEFAULT_CREDENTIAL_FILE);
73
- }
74
- /** One `key=value` request from git on stdin. */
75
- function readRequest() {
76
- const text = readFileSync(0, 'utf8');
77
- const request = new Map();
78
- for (const line of text.split('\n')) {
79
- const at = line.indexOf('=');
80
- if (at > 0)
81
- request.set(line.slice(0, at), line.slice(at + 1));
82
- }
83
- return request;
84
- }
85
- /** The stored credential for one host, or undefined when there is none. */
86
- function readCredential(host) {
87
- let parsed;
88
- try {
89
- parsed = JSON.parse(readFileSync(credentialFile(), 'utf8'));
90
- }
91
- catch (error) {
92
- debug(`read failed: ${String(error)}`);
93
- return undefined;
94
- }
95
- const credentials = parsed.credentials;
96
- if (!Array.isArray(credentials))
97
- return undefined;
98
- const entry = credentials.find((item) => typeof item === 'object' && item !== null && item.host === host);
99
- if (entry === undefined)
100
- return undefined;
101
- const secret = typeof entry.secret === 'string' ? entry.secret : '';
102
- if (secret === '')
103
- return undefined;
104
- const username = typeof entry.username === 'string' && entry.username.trim() !== ''
105
- ? entry.username
106
- : TOKEN_USERNAME;
107
- return { username, secret };
108
- }
109
- if (OPERATION === 'get') {
110
- const request = readRequest();
111
- const host = request.get('host') ?? '';
112
- debug(`get host=${host} protocol=${request.get('protocol') ?? ''}`);
113
- const credential = readCredential(host);
114
- if (credential !== undefined) {
115
- process.stdout.write(`username=${credential.username}\npassword=${credential.secret}\n`);
116
- }
117
- // A miss stays silent: git tries the next helper in the chain.
118
- }
119
- // `store` / `erase` are deliberate no-ops (see the header comment).