@passioncode-ai/fabric-agent-adapter 0.81.0 → 0.83.1
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/CHANGELOG.md +200 -3
- package/README.md +11 -6
- package/SECURITY.md +8 -5
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +7 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +4 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +18 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +25 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +31 -15
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +274 -17
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +24 -151
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check_cli.py +164 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_tree.py +3 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +17 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +24 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +14 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/README.md +76 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/config.example.json +28 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/data_agent.py +366 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +32 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +4 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support-agent.mjs +115 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +63 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/a2a.md +98 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +23 -10
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/connectors.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +15 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +14 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +6 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +28 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/ops-digest.md +75 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +7 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +30 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +10 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +70 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_a2a.py +158 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_channel_agent.py +7 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +22 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a-server.mjs +274 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a.mjs +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-fixture.mjs +14 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-link.mjs +54 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +45 -31
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-slots.mjs +16 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-connections.mjs +29 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +5 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +18 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-events.mjs +6 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-holds.mjs +27 -10
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +3 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +21 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +20 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +5 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +88 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +5 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-openrouter.mjs +24 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ops-digest.mjs +455 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +17 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +18 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +133 -30
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +44 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +3 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-socket-proof.mjs +180 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +5 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-tailnet-identity.mjs +468 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a.py +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a_server.py +404 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +6 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_fixture.py +16 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py +97 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +35 -48
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_slots.py +15 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +35 -159
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +18 -13
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +23 -14
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +43 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +21 -18
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +39 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +55 -96
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +98 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +92 -10
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_openrouter.py +43 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ops_digest.py +647 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +16 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +20 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +180 -162
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +274 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +3 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_socket_proof.py +390 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +3 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_tailnet_identity.py +492 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +465 -55
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample-remote-service.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +16 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/tailnet-groups.schema.json +69 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +5 -4
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md +16 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-that-answer-people.md +14 -9
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +4 -2
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +18 -4
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +2 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/admit.py +162 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +2 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +30 -17
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +53 -35
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +10 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +2 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +34 -21
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +96 -15
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_paths.py +147 -45
package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_paths.py
CHANGED
|
@@ -8,6 +8,7 @@ from pathlib import Path, PurePath
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
from typing import List
|
|
10
10
|
from typing import Optional
|
|
11
|
+
from typing import Tuple
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
_DRIVE_MOUNT = re.compile(r"/(?:cygdrive/)?([A-Za-z])(?=/|$)")
|
|
@@ -28,13 +29,130 @@ def _windows_path(token: str) -> Optional[str]:
|
|
|
28
29
|
return ntpath.normpath(token)
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
_OPERATORS = frozenset("();&|<>")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _heredoc_word(command: str, at: int) -> Tuple[str, int]:
|
|
36
|
+
"""A here-document's delimiter word from `at` (blanks first), its quotes taken off as bash does, and where it ends."""
|
|
37
|
+
while at < len(command) and command[at] in " \t":
|
|
38
|
+
at += 1
|
|
39
|
+
word, quote = [], None
|
|
40
|
+
while at < len(command):
|
|
41
|
+
char = command[at]
|
|
42
|
+
if quote:
|
|
43
|
+
if char == quote:
|
|
44
|
+
quote = None
|
|
45
|
+
else:
|
|
46
|
+
word.append(char)
|
|
47
|
+
elif char in "'\"":
|
|
48
|
+
quote = char
|
|
49
|
+
elif char == "\\" and at + 1 < len(command):
|
|
50
|
+
at += 1
|
|
51
|
+
word.append(command[at])
|
|
52
|
+
elif char in " \t\n" or char in _OPERATORS:
|
|
53
|
+
break
|
|
54
|
+
else:
|
|
55
|
+
word.append(char)
|
|
56
|
+
at += 1
|
|
57
|
+
return "".join(word), at
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def prepare(command: str) -> str:
|
|
61
|
+
"""The command as bash reads its words, before the lexer (FAA-187): a `\\` and newline outside single quotes and
|
|
62
|
+
outside a comment joins the lines (an escaped backslash before a newline joins nothing, (7)); a comment, a `#` that
|
|
63
|
+
starts a word, is cut to its line's end (a `#` inside a word is the word's, (2)); a here-document's body and its
|
|
64
|
+
terminator line are cut out and the redirection left as a here-string, so a quote in a body never breaks the lexer
|
|
65
|
+
(5), with bash's own terminator rules: the delimiter word as written, its quotes taken off, `<<-` stripping leading
|
|
66
|
+
tabs, a plain `<<` matching only the bare line, and `<<` inside `((` `))` a shift (9); and a quoted word made only of
|
|
67
|
+
operator characters (`'&'`, `")"`) becomes a plain word, so the lexer never reads it as an operator (1)."""
|
|
68
|
+
out: List[str] = []
|
|
69
|
+
at, size, quote, word_start, arithmetic = 0, len(command), None, True, 0
|
|
70
|
+
pending: List[Tuple[str, bool]] = []
|
|
71
|
+
while at < size:
|
|
72
|
+
char = command[at]
|
|
73
|
+
if quote == "'":
|
|
74
|
+
out.append(char)
|
|
75
|
+
quote = None if char == "'" else quote
|
|
76
|
+
at += 1
|
|
77
|
+
continue
|
|
78
|
+
if char == "\\":
|
|
79
|
+
if command[at + 1:at + 2] == "\n":
|
|
80
|
+
at += 2 # a line continuation (outside single quotes)
|
|
81
|
+
continue
|
|
82
|
+
out.append(command[at:at + 2])
|
|
83
|
+
at, word_start = at + 2, False
|
|
84
|
+
continue
|
|
85
|
+
if quote == '"':
|
|
86
|
+
out.append(char)
|
|
87
|
+
quote = None if char == '"' else quote
|
|
88
|
+
at += 1
|
|
89
|
+
continue
|
|
90
|
+
if char in "'\"":
|
|
91
|
+
end = command.find(char, at + 1) if char == "'" else -1
|
|
92
|
+
if char == '"':
|
|
93
|
+
look = at + 1
|
|
94
|
+
while look < size and command[look] != '"':
|
|
95
|
+
look += 2 if command[look] == "\\" else 1
|
|
96
|
+
end = look if look < size else -1
|
|
97
|
+
inner = command[at + 1:end] if end > at else ""
|
|
98
|
+
after = command[end + 1:end + 2] if end > at else ""
|
|
99
|
+
if word_start and inner and set(inner) <= _OPERATORS and (after == "" or after in " \t\n" or after in _OPERATORS):
|
|
100
|
+
out.append("_") # a quoted operator is a word, never an operator (1)
|
|
101
|
+
at, word_start = end + 1, False
|
|
102
|
+
continue
|
|
103
|
+
quote, word_start = char, False
|
|
104
|
+
out.append(char)
|
|
105
|
+
at += 1
|
|
106
|
+
continue
|
|
107
|
+
if char == "#" and word_start:
|
|
108
|
+
while at < size and command[at] != "\n":
|
|
109
|
+
at += 1 # a comment, to its line's end; the newline stays
|
|
110
|
+
continue
|
|
111
|
+
if command.startswith("((", at):
|
|
112
|
+
arithmetic += 1
|
|
113
|
+
elif command.startswith("))", at) and arithmetic:
|
|
114
|
+
arithmetic -= 1
|
|
115
|
+
if command.startswith("<<<", at): # a here-string: its word is text, no body follows
|
|
116
|
+
out.append("<<<")
|
|
117
|
+
at, word_start = at + 3, True
|
|
118
|
+
continue
|
|
119
|
+
if command.startswith("<<", at) and not arithmetic:
|
|
120
|
+
strip = command[at + 2:at + 3] == "-"
|
|
121
|
+
word, end = _heredoc_word(command, at + 3 if strip else at + 2)
|
|
122
|
+
if word:
|
|
123
|
+
pending.append((word, strip))
|
|
124
|
+
out.append("<<< _ ") # the body is text for the command: a here-string stands in for it
|
|
125
|
+
at, word_start = end, True
|
|
126
|
+
continue
|
|
127
|
+
if char == "\n" and pending:
|
|
128
|
+
out.append("\n")
|
|
129
|
+
line_start = at + 1
|
|
130
|
+
for delimiter, strip in pending:
|
|
131
|
+
while line_start <= size:
|
|
132
|
+
line_end = command.find("\n", line_start)
|
|
133
|
+
line_end = size if line_end < 0 else line_end
|
|
134
|
+
line = command[line_start:line_end]
|
|
135
|
+
line_start = line_end + 1
|
|
136
|
+
if (line.lstrip("\t") if strip else line) == delimiter:
|
|
137
|
+
break
|
|
138
|
+
pending = []
|
|
139
|
+
at, word_start = line_start - 1, True # at the terminator line's newline: it ends the command
|
|
140
|
+
if at >= size:
|
|
141
|
+
break
|
|
142
|
+
continue
|
|
143
|
+
out.append(char)
|
|
144
|
+
word_start = char in " \t\n" or char in _OPERATORS
|
|
145
|
+
at += 1
|
|
146
|
+
return "".join(out)
|
|
147
|
+
|
|
148
|
+
|
|
31
149
|
#: Words after which a command word may still stand (reserved words and the prefixes that run their command here).
|
|
32
150
|
KEYWORDS = frozenset(("{", "!", "then", "do", "else", "elif", "if", "while", "until", "time", "command", "builtin"))
|
|
33
151
|
ASSIGNMENT = re.compile(r"[A-Za-z_][A-Za-z0-9_]*=")
|
|
34
152
|
|
|
35
153
|
|
|
36
154
|
def bash_paths(command: str, cwd: Path, windows: Optional[bool] = None,
|
|
37
|
-
ends: Optional[List[Optional[str]]] = None) -> List[str]:
|
|
155
|
+
ends: Optional[List[Optional[str]]] = None, fold_case: Optional[bool] = None) -> List[str]:
|
|
38
156
|
"""The folders a Bash command names as `cd <absolute path>` or `git -C <path>`, in order. A relative `-C` is read
|
|
39
157
|
against the folder the command last changed into with an absolute `cd`, else `cwd`; after a `cd` to a relative or
|
|
40
158
|
unknown folder it is dropped (FAA-166). A `cd` inside `( )`, a pipeline's stage or a list sent to the background
|
|
@@ -44,19 +162,22 @@ def bash_paths(command: str, cwd: Path, windows: Optional[bool] = None,
|
|
|
44
162
|
`sys.platform == "win32"`) paths are read as Git Bash writes them, with its quoting: an unquoted `C:\\work` is
|
|
45
163
|
`C:work` there too, so a native path counts when it is quoted (FAA-175); Cygwin and MSYS2 Pythons read their own
|
|
46
164
|
POSIX paths (FAA-177). With `ends`, the folder the command leaves the shell in is appended to it: an absolute path,
|
|
47
|
-
or None when it is not known (a relative cd, popd, an unclosed subshell, a command that does not split; FAA-184).
|
|
165
|
+
or None when it is not known (a relative cd, popd, an unclosed subshell, a command that does not split; FAA-184).
|
|
166
|
+
`prepare` reads comments, line continuations, here-documents and quoted operators first (FAA-187); a line ending
|
|
167
|
+
in `|`, `|&`, `&&` or `||` goes on on the next, as in bash (6); a `-C` word bash would expand (`$`, a backtick, a
|
|
168
|
+
leading `~`) is a folder not known here (8), and `-C ""` runs where the shell is; `git` is matched without case on
|
|
169
|
+
Windows and, with `fold_case` (default: on macOS), where the file system finds `GIT` as git (11). A `case`
|
|
170
|
+
pattern's `)` inside a subshell is still read as closing it."""
|
|
48
171
|
import shlex # only a Stop hook with a transcript needs it
|
|
49
172
|
windows = sys.platform == "win32" if windows is None else windows
|
|
173
|
+
fold_case = windows or (sys.platform == "darwin" if fold_case is None else fold_case)
|
|
50
174
|
if windows:
|
|
51
175
|
import ntpath as paths
|
|
52
176
|
else:
|
|
53
177
|
import posixpath as paths
|
|
54
178
|
try:
|
|
55
|
-
|
|
56
|
-
#
|
|
57
|
-
# the line end a here-document counts.
|
|
58
|
-
command = command.replace("\\\n", "").replace("\n", "\n;\n")
|
|
59
|
-
lexer = shlex.shlex(command, posix=True, punctuation_chars="();<>|&\n")
|
|
179
|
+
lexer = shlex.shlex(prepare(command), posix=True, punctuation_chars="();<>|&\n")
|
|
180
|
+
lexer.commenters = "" # prepare cut the comments, where bash reads them (a `#` inside a word is the word's)
|
|
60
181
|
lexer.whitespace = " \t\r" # a newline ends a command, as `;` does
|
|
61
182
|
lexer.whitespace_split = True
|
|
62
183
|
tokens = list(lexer)
|
|
@@ -87,61 +208,39 @@ def bash_paths(command: str, cwd: Path, windows: Optional[bool] = None,
|
|
|
87
208
|
base = pipe_start
|
|
88
209
|
pipe_start, in_pipeline = base, False
|
|
89
210
|
|
|
90
|
-
heredocs: List[str] = [] # the delimiters of here-documents whose bodies start at the next newline
|
|
91
|
-
|
|
92
|
-
def past_heredocs(at: int) -> int:
|
|
93
|
-
"""The index of the newline ending the last pending here-document's delimiter line (or the end): the body is
|
|
94
|
-
text for a command, never commands of its own."""
|
|
95
|
-
while heredocs and at < len(tokens):
|
|
96
|
-
line_end = at
|
|
97
|
-
while line_end < len(tokens) and "\n" not in tokens[line_end]:
|
|
98
|
-
line_end += 1
|
|
99
|
-
if tokens[at:line_end] == [heredocs[0]]:
|
|
100
|
-
heredocs.pop(0)
|
|
101
|
-
if not heredocs:
|
|
102
|
-
return line_end
|
|
103
|
-
at = line_end + 1
|
|
104
|
-
heredocs.clear()
|
|
105
|
-
return len(tokens)
|
|
106
|
-
|
|
107
211
|
start = True # a command word may stand here
|
|
212
|
+
continued = False # the last operator was `|`, `|&`, `&&` or `||`: a newline after it goes on (FAA-187 (6))
|
|
108
213
|
index = 0
|
|
109
214
|
while index < len(tokens):
|
|
110
215
|
token = tokens[index]
|
|
111
216
|
if token and set(token) <= separators:
|
|
112
|
-
at, ends_command
|
|
217
|
+
at, ends_command = 0, False
|
|
113
218
|
while at < len(token):
|
|
114
219
|
pair, char = token[at:at + 2], token[at]
|
|
220
|
+
if char == "\n" and continued:
|
|
221
|
+
at += 1 # the line ended in a pipe or a list operator: the command goes on
|
|
222
|
+
continue
|
|
115
223
|
if pair in ("&&", "||"):
|
|
116
224
|
end_pipeline()
|
|
117
|
-
at, ends_command = at + 2, True
|
|
225
|
+
at, ends_command, continued = at + 2, True, True
|
|
118
226
|
continue
|
|
119
|
-
if token.startswith("<<<", at): # a here-string: its word is text
|
|
227
|
+
if token.startswith("<<<", at): # a here-string (prepare leaves one for a here-document): its word is text
|
|
120
228
|
at += 3
|
|
121
229
|
continue
|
|
122
|
-
if pair == "<<" and index + 1 < len(tokens):
|
|
123
|
-
delimiter = tokens[index + 1] # <<EOF, <<'EOF', <<-EOF: shlex has taken the quotes off
|
|
124
|
-
if delimiter == "-" and index + 2 < len(tokens):
|
|
125
|
-
index, delimiter = index + 1, tokens[index + 2]
|
|
126
|
-
heredocs.append(delimiter[1:] if delimiter.startswith("-") and len(delimiter) > 1 else delimiter)
|
|
127
|
-
index += 1 # the delimiter is no command word
|
|
128
|
-
at += 2
|
|
129
|
-
continue
|
|
130
230
|
if pair == "&>": # `&>` and `&>>` redirect, they background nothing
|
|
131
231
|
at += 3 if token[at + 2:at + 3] == ">" else 2
|
|
132
232
|
continue
|
|
133
233
|
if char in "<>": # a redirection: `2>&1` backgrounds nothing, `>|` pipes nothing
|
|
134
234
|
at += 2 if token[at + 1:at + 2] in ("&", "|") else 1
|
|
135
235
|
continue
|
|
136
|
-
ends_command = True
|
|
236
|
+
ends_command, continued = True, False
|
|
137
237
|
if pair == "|&" or char == "|":
|
|
138
|
-
base, in_pipeline = pipe_start, True
|
|
238
|
+
base, in_pipeline, continued = pipe_start, True, True
|
|
139
239
|
at += 2 if pair == "|&" else 1
|
|
140
240
|
continue
|
|
141
241
|
if char in ";\n":
|
|
142
242
|
end_pipeline()
|
|
143
243
|
list_start = base
|
|
144
|
-
newline = newline or char == "\n"
|
|
145
244
|
elif char == "&":
|
|
146
245
|
end_pipeline()
|
|
147
246
|
base = pipe_start = list_start
|
|
@@ -155,8 +254,9 @@ def bash_paths(command: str, cwd: Path, windows: Optional[bool] = None,
|
|
|
155
254
|
base, pipe_start, list_start, in_pipeline = stack.pop()
|
|
156
255
|
at += 1
|
|
157
256
|
start = start or ends_command # a redirection alone starts no command
|
|
158
|
-
index
|
|
257
|
+
index += 1
|
|
159
258
|
continue
|
|
259
|
+
continued = False
|
|
160
260
|
if start and (token in KEYWORDS or ASSIGNMENT.match(token)):
|
|
161
261
|
index += 1 # `{ cd /a; }`, `then cd`, `x=1 cd`, `command cd`: a command word may still follow
|
|
162
262
|
continue
|
|
@@ -172,14 +272,16 @@ def bash_paths(command: str, cwd: Path, windows: Optional[bool] = None,
|
|
|
172
272
|
if folder:
|
|
173
273
|
found.append(folder)
|
|
174
274
|
base = folder
|
|
175
|
-
elif start and (paths.basename(token).lower() if
|
|
275
|
+
elif start and (paths.basename(token).lower() if fold_case else paths.basename(token)) in ("git", "git.exe"):
|
|
176
276
|
look, at, moved = index + 1, base, False
|
|
177
277
|
while look < len(tokens) and tokens[look].startswith("-") and not set(tokens[look]) <= separators:
|
|
178
|
-
if tokens[look] == "-C" and look + 1 < len(tokens) and not set(tokens[look + 1]) <= separators:
|
|
179
|
-
# git reads each relative -C against the one before it and runs in the last
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
278
|
+
if tokens[look] == "-C" and look + 1 < len(tokens) and (tokens[look + 1] == "" or not set(tokens[look + 1]) <= separators):
|
|
279
|
+
# git reads each relative -C against the one before it and runs in the last; `-C ""` where it is
|
|
280
|
+
word = tokens[look + 1]
|
|
281
|
+
expands = "$" in word or "`" in word or word.startswith("~") # a folder not known here (FAA-187 (8))
|
|
282
|
+
folder = None if expands else absolute(word)
|
|
283
|
+
if folder is None and not expands and at is not None and not word.startswith("/"):
|
|
284
|
+
folder = paths.normpath(paths.join(at, word))
|
|
183
285
|
if windows and not (paths.isabs(folder) and paths.splitdrive(folder)[0]):
|
|
184
286
|
folder = None # `C:work`, a drive-relative path: not known here
|
|
185
287
|
at, moved = folder, True
|