@portel/photon 1.7.0 → 1.8.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 +23 -24
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +117 -42
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/design-system/tokens.d.ts +1 -1
- package/dist/auto-ui/design-system/tokens.d.ts.map +1 -1
- package/dist/auto-ui/design-system/tokens.js +1 -1
- package/dist/auto-ui/design-system/tokens.js.map +1 -1
- package/dist/auto-ui/frontend/index.html +1 -1
- package/dist/auto-ui/rendering/components.d.ts.map +1 -1
- package/dist/auto-ui/rendering/components.js +568 -0
- package/dist/auto-ui/rendering/components.js.map +1 -1
- package/dist/auto-ui/rendering/field-analyzer.d.ts +56 -0
- package/dist/auto-ui/rendering/field-analyzer.d.ts.map +1 -1
- package/dist/auto-ui/rendering/field-analyzer.js +177 -0
- package/dist/auto-ui/rendering/field-analyzer.js.map +1 -1
- package/dist/auto-ui/rendering/layout-selector.d.ts +14 -2
- package/dist/auto-ui/rendering/layout-selector.d.ts.map +1 -1
- package/dist/auto-ui/rendering/layout-selector.js +125 -1
- package/dist/auto-ui/rendering/layout-selector.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +353 -19
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +7 -1
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam.bundle.js +22441 -4216
- package/dist/beam.bundle.js.map +4 -4
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +37 -0
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/package.d.ts.map +1 -1
- package/dist/cli/commands/package.js +16 -0
- package/dist/cli/commands/package.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +628 -14
- package/dist/cli.js.map +1 -1
- package/dist/context-store.d.ts +79 -0
- package/dist/context-store.d.ts.map +1 -0
- package/dist/context-store.js +210 -0
- package/dist/context-store.js.map +1 -0
- package/dist/daemon/client.d.ts +13 -4
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +138 -77
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/manager.d.ts +0 -25
- package/dist/daemon/manager.d.ts.map +1 -1
- package/dist/daemon/manager.js +10 -38
- package/dist/daemon/manager.js.map +1 -1
- package/dist/daemon/protocol.d.ts +7 -2
- package/dist/daemon/protocol.d.ts.map +1 -1
- package/dist/daemon/protocol.js.map +1 -1
- package/dist/daemon/server.js +257 -35
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/session-manager.d.ts +24 -4
- package/dist/daemon/session-manager.d.ts.map +1 -1
- package/dist/daemon/session-manager.js +62 -12
- package/dist/daemon/session-manager.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +3 -20
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +53 -75
- package/dist/loader.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +258 -218
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts +2 -0
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +42 -6
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/photons/maker.photon.d.ts.map +1 -1
- package/dist/photons/maker.photon.js +3 -1
- package/dist/photons/maker.photon.js.map +1 -1
- package/dist/photons/maker.photon.ts +3 -1
- package/dist/serv/index.d.ts.map +1 -1
- package/dist/serv/index.js.map +1 -1
- package/dist/server.d.ts +32 -15
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +468 -469
- package/dist/server.js.map +1 -1
- package/dist/shared/security.d.ts.map +1 -1
- package/dist/shared/security.js +4 -8
- package/dist/shared/security.js.map +1 -1
- package/dist/shell-completions.d.ts +21 -0
- package/dist/shell-completions.d.ts.map +1 -0
- package/dist/shell-completions.js +102 -0
- package/dist/shell-completions.js.map +1 -0
- package/dist/template-manager.d.ts.map +1 -1
- package/dist/template-manager.js.map +1 -1
- package/package.json +11 -7
package/dist/cli.js
CHANGED
|
@@ -24,9 +24,9 @@ import { toEnvVarName } from './shared/config-docs.js';
|
|
|
24
24
|
import { runTask } from './shared/task-runner.js';
|
|
25
25
|
import { normalizeLogLevel, logger } from './shared/logger.js';
|
|
26
26
|
import { printHeader, printInfo, printWarning, printError, printSuccess } from './cli-formatter.js';
|
|
27
|
-
import { handleError, getErrorMessage, ExitCode, exitWithError, } from './shared/error-handler.js';
|
|
27
|
+
import { handleError, getErrorMessage, ExitCode, exitWithError, isNodeError, } from './shared/error-handler.js';
|
|
28
28
|
import { validateOrThrow, inRange, isPositive, isInteger } from './shared/validation.js';
|
|
29
|
-
import { createReadline, promptWait } from './shared/cli-utils.js';
|
|
29
|
+
import { createReadline, promptText, promptWait } from './shared/cli-utils.js';
|
|
30
30
|
import { registerMarketplaceCommands } from './cli/commands/marketplace.js';
|
|
31
31
|
import { registerInfoCommand } from './cli/commands/info.js';
|
|
32
32
|
import { registerPackageCommands } from './cli/commands/package.js';
|
|
@@ -259,7 +259,10 @@ async function handleUrlElicitation(ask) {
|
|
|
259
259
|
execFile(openCommand, [ask.url]);
|
|
260
260
|
}
|
|
261
261
|
catch (error) {
|
|
262
|
-
|
|
262
|
+
const msg = isNodeError(error, 'ENOENT')
|
|
263
|
+
? `Could not find '${openCommand}' to open URLs`
|
|
264
|
+
: getErrorMessage(error);
|
|
265
|
+
cliHint(`Could not open browser: ${msg}. Please open the URL manually.`);
|
|
263
266
|
}
|
|
264
267
|
const shouldContinue = await promptWait('Press Enter when done', true);
|
|
265
268
|
return { action: shouldContinue ? 'accept' : 'cancel' };
|
|
@@ -758,6 +761,9 @@ program
|
|
|
758
761
|
.configureHelp({
|
|
759
762
|
sortSubcommands: false,
|
|
760
763
|
sortOptions: false,
|
|
764
|
+
// Hide Commander's auto-generated "Commands:" section since we show
|
|
765
|
+
// a custom categorized section in addHelpText('after', ...)
|
|
766
|
+
visibleCommands: () => [],
|
|
761
767
|
})
|
|
762
768
|
.addHelpText('after', `
|
|
763
769
|
Runtime Commands:
|
|
@@ -767,6 +773,11 @@ Runtime Commands:
|
|
|
767
773
|
beam Launch Photon Beam (interactive control panel)
|
|
768
774
|
serve Start local multi-tenant MCP hosting for development
|
|
769
775
|
|
|
776
|
+
Configuration:
|
|
777
|
+
use <photon> [instance] Switch to a named instance of a stateful photon
|
|
778
|
+
instances <photon> List all instances of a stateful photon
|
|
779
|
+
set <photon> [values] Configure environment for a photon
|
|
780
|
+
|
|
770
781
|
Hosting:
|
|
771
782
|
host <command> Manage cloud hosting (preview, deploy)
|
|
772
783
|
|
|
@@ -1113,8 +1124,12 @@ program
|
|
|
1113
1124
|
logger.debug(`Could not auto-open browser: ${err.message}`);
|
|
1114
1125
|
});
|
|
1115
1126
|
}
|
|
1116
|
-
// Handle shutdown signals
|
|
1127
|
+
// Handle shutdown signals (guard against duplicate Ctrl+C)
|
|
1128
|
+
let shuttingDown = false;
|
|
1117
1129
|
const shutdown = async () => {
|
|
1130
|
+
if (shuttingDown)
|
|
1131
|
+
return;
|
|
1132
|
+
shuttingDown = true;
|
|
1118
1133
|
console.error('\nShutting down Photon Beam...');
|
|
1119
1134
|
// Gracefully close external MCP clients to prevent ugly tracebacks
|
|
1120
1135
|
try {
|
|
@@ -1363,11 +1378,15 @@ maker
|
|
|
1363
1378
|
// Check if file already exists
|
|
1364
1379
|
try {
|
|
1365
1380
|
await fs.access(filePath);
|
|
1366
|
-
|
|
1367
|
-
|
|
1381
|
+
exitWithError(`File already exists: ${filePath}`, {
|
|
1382
|
+
suggestion: `Choose a different name or delete the existing file`,
|
|
1383
|
+
});
|
|
1368
1384
|
}
|
|
1369
|
-
catch {
|
|
1370
|
-
|
|
1385
|
+
catch (err) {
|
|
1386
|
+
if (!isNodeError(err, 'ENOENT')) {
|
|
1387
|
+
exitWithError(`Cannot access ${filePath}: ${getErrorMessage(err)}`);
|
|
1388
|
+
}
|
|
1389
|
+
// ENOENT = file doesn't exist — good, proceed
|
|
1371
1390
|
}
|
|
1372
1391
|
// Read template
|
|
1373
1392
|
const templatePath = path.join(__dirname, '..', 'templates', 'photon.template.ts');
|
|
@@ -1375,8 +1394,8 @@ maker
|
|
|
1375
1394
|
try {
|
|
1376
1395
|
template = await fs.readFile(templatePath, 'utf-8');
|
|
1377
1396
|
}
|
|
1378
|
-
catch {
|
|
1379
|
-
|
|
1397
|
+
catch (err) {
|
|
1398
|
+
logger.debug(`Template not found at ${templatePath}, using inline template`);
|
|
1380
1399
|
template = getInlineTemplate();
|
|
1381
1400
|
}
|
|
1382
1401
|
// Replace placeholders
|
|
@@ -1824,6 +1843,590 @@ SEE ALSO:
|
|
|
1824
1843
|
await runMethod(photon, method, args);
|
|
1825
1844
|
}
|
|
1826
1845
|
});
|
|
1846
|
+
// Use command: switch to a named instance of a stateful photon
|
|
1847
|
+
program
|
|
1848
|
+
.command('use')
|
|
1849
|
+
.argument('<photon>', 'Photon name')
|
|
1850
|
+
.argument('[instance]', 'Instance name (omit for default)')
|
|
1851
|
+
.description('Switch to a named instance of a stateful photon')
|
|
1852
|
+
.action(async (photonName, instance) => {
|
|
1853
|
+
try {
|
|
1854
|
+
const { CLISessionStore } = await import('./context-store.js');
|
|
1855
|
+
// Write to CLI session store only — each client manages its own instance
|
|
1856
|
+
new CLISessionStore().setCurrentInstance(photonName, instance || '');
|
|
1857
|
+
const label = instance || 'default';
|
|
1858
|
+
printSuccess(`${photonName} → instance: ${label}`);
|
|
1859
|
+
// Refresh completions cache (picks up new instance)
|
|
1860
|
+
try {
|
|
1861
|
+
const { generateCompletionCache } = await import('./shell-completions.js');
|
|
1862
|
+
await generateCompletionCache();
|
|
1863
|
+
}
|
|
1864
|
+
catch {
|
|
1865
|
+
// Best-effort: don't break the use command if cache refresh fails
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
catch (error) {
|
|
1869
|
+
printError(getErrorMessage(error));
|
|
1870
|
+
process.exit(1);
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1873
|
+
// Instances command: list all instances of a stateful photon
|
|
1874
|
+
program
|
|
1875
|
+
.command('instances')
|
|
1876
|
+
.argument('<photon>', 'Photon name')
|
|
1877
|
+
.description('List all instances of a stateful photon')
|
|
1878
|
+
.action(async (photonName) => {
|
|
1879
|
+
try {
|
|
1880
|
+
const { InstanceStore } = await import('./context-store.js');
|
|
1881
|
+
const store = new InstanceStore();
|
|
1882
|
+
const instances = store.listInstances(photonName);
|
|
1883
|
+
const current = store.getCurrentInstance(photonName) || 'default';
|
|
1884
|
+
if (instances.length === 0) {
|
|
1885
|
+
printInfo(`No instances found for ${photonName}.`);
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
cliHeading(`${photonName} — Instances`);
|
|
1889
|
+
cliSpacer();
|
|
1890
|
+
for (const name of instances) {
|
|
1891
|
+
const marker = name === current ? ' ← current' : '';
|
|
1892
|
+
console.log(` ${name}${marker}`);
|
|
1893
|
+
}
|
|
1894
|
+
cliSpacer();
|
|
1895
|
+
}
|
|
1896
|
+
catch (error) {
|
|
1897
|
+
printError(getErrorMessage(error));
|
|
1898
|
+
process.exit(1);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
// Shell command group: shell integration utilities
|
|
1902
|
+
const shell = program.command('shell').description('Shell integration utilities');
|
|
1903
|
+
shell
|
|
1904
|
+
.command('init')
|
|
1905
|
+
.option('--hook', 'Output the shell hook script (used internally by eval/Invoke-Expression)')
|
|
1906
|
+
.description('Set up shell integration for direct photon commands and tab completion')
|
|
1907
|
+
.action(async (options) => {
|
|
1908
|
+
// Detect shell type
|
|
1909
|
+
const userShell = process.env.SHELL || '';
|
|
1910
|
+
const isPowerShell = !!process.env.PSModulePath;
|
|
1911
|
+
const isZsh = !isPowerShell && userShell.includes('zsh');
|
|
1912
|
+
const isBash = !isPowerShell && userShell.includes('bash');
|
|
1913
|
+
let shellType = 'unsupported';
|
|
1914
|
+
if (isZsh)
|
|
1915
|
+
shellType = 'zsh';
|
|
1916
|
+
else if (isBash)
|
|
1917
|
+
shellType = 'bash';
|
|
1918
|
+
else if (isPowerShell || process.platform === 'win32')
|
|
1919
|
+
shellType = 'powershell';
|
|
1920
|
+
// Unsupported shell — show supported list and exit
|
|
1921
|
+
if (shellType === 'unsupported') {
|
|
1922
|
+
const detected = userShell ? path.basename(userShell) : 'unknown';
|
|
1923
|
+
printError(`Unsupported shell: ${detected}`);
|
|
1924
|
+
console.log('');
|
|
1925
|
+
console.log(' Supported shells:');
|
|
1926
|
+
console.log(' zsh ~/.zshrc (macOS default)');
|
|
1927
|
+
console.log(' bash ~/.bashrc (Linux default)');
|
|
1928
|
+
console.log(' PowerShell $PROFILE (Windows default, cross-platform)');
|
|
1929
|
+
console.log('');
|
|
1930
|
+
console.log(' To use a specific shell, set $SHELL and retry:');
|
|
1931
|
+
console.log(' SHELL=/bin/zsh photon shell init');
|
|
1932
|
+
process.exit(1);
|
|
1933
|
+
}
|
|
1934
|
+
// RC file and eval/invoke line per shell
|
|
1935
|
+
let rcFile;
|
|
1936
|
+
let evalLine;
|
|
1937
|
+
const marker = '# photon shell integration';
|
|
1938
|
+
if (shellType === 'powershell') {
|
|
1939
|
+
// PowerShell profile path: cross-platform
|
|
1940
|
+
rcFile =
|
|
1941
|
+
process.platform === 'win32'
|
|
1942
|
+
? path.join(os.homedir(), 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1')
|
|
1943
|
+
: path.join(os.homedir(), '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1');
|
|
1944
|
+
evalLine = 'Invoke-Expression (& photon shell init --hook)';
|
|
1945
|
+
}
|
|
1946
|
+
else {
|
|
1947
|
+
rcFile = isZsh ? path.join(os.homedir(), '.zshrc') : path.join(os.homedir(), '.bashrc');
|
|
1948
|
+
evalLine = 'eval "$(photon shell init --hook)"';
|
|
1949
|
+
}
|
|
1950
|
+
// --hook flag: output the hook script
|
|
1951
|
+
if (options.hook) {
|
|
1952
|
+
const photonDir = path.join(os.homedir(), '.photon');
|
|
1953
|
+
let photonNames = [];
|
|
1954
|
+
try {
|
|
1955
|
+
const entries = await fs.readdir(photonDir);
|
|
1956
|
+
photonNames = entries
|
|
1957
|
+
.filter((e) => /\.photon\.(ts|js)$/.test(e))
|
|
1958
|
+
.map((e) => e.replace(/\.photon\.(ts|js)$/, ''));
|
|
1959
|
+
}
|
|
1960
|
+
catch {
|
|
1961
|
+
// ~/.photon/ doesn't exist yet
|
|
1962
|
+
}
|
|
1963
|
+
if (shellType === 'zsh') {
|
|
1964
|
+
const functions = photonNames
|
|
1965
|
+
.map((name) => `${name}() { photon cli ${name} "$@"; }`)
|
|
1966
|
+
.join('\n');
|
|
1967
|
+
console.log(`${marker}
|
|
1968
|
+
|
|
1969
|
+
# Shell functions for installed photons (direct invocation)
|
|
1970
|
+
${functions}
|
|
1971
|
+
|
|
1972
|
+
# Fallback for newly installed photons (before shell restart)
|
|
1973
|
+
command_not_found_handler() {
|
|
1974
|
+
if [ -f "$HOME/.photon/\$1.photon.ts" ] || [ -f "$HOME/.photon/\$1.photon.js" ]; then
|
|
1975
|
+
photon cli "$@"
|
|
1976
|
+
return $?
|
|
1977
|
+
fi
|
|
1978
|
+
echo "zsh: command not found: \$1" >&2
|
|
1979
|
+
return 127
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
# Tab completion for photon methods, params, and instances
|
|
1983
|
+
_photon_cache="$HOME/.photon/cache/completions.cache"
|
|
1984
|
+
|
|
1985
|
+
_photon_complete_direct() {
|
|
1986
|
+
local cmd="\$words[1]"
|
|
1987
|
+
local curcontext="\$curcontext" state line
|
|
1988
|
+
_arguments -C "1: :->method" "*::arg:->params"
|
|
1989
|
+
case "\$state" in
|
|
1990
|
+
method)
|
|
1991
|
+
if [[ -f "\$_photon_cache" ]]; then
|
|
1992
|
+
local -a methods
|
|
1993
|
+
methods=("\${(@f)$(grep "^method:\${cmd}:" "\$_photon_cache" | while IFS=: read -r _ _ name desc; do echo "\${name}:\${desc}"; done)}")
|
|
1994
|
+
_describe 'method' methods
|
|
1995
|
+
fi
|
|
1996
|
+
;;
|
|
1997
|
+
params)
|
|
1998
|
+
if [[ -f "\$_photon_cache" ]]; then
|
|
1999
|
+
local method="\$line[1]"
|
|
2000
|
+
local -a params
|
|
2001
|
+
params=("\${(@f)$(grep "^param:\${cmd}:\${method}:" "\$_photon_cache" | while IFS=: read -r _ _ _ name type req; do echo "--\${name}[\${type}]"; done)}")
|
|
2002
|
+
_describe 'parameter' params
|
|
2003
|
+
fi
|
|
2004
|
+
;;
|
|
2005
|
+
esac
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
# Register completion for each photon function (guard for non-interactive shells)
|
|
2009
|
+
if (( $+functions[compdef] )); then
|
|
2010
|
+
${photonNames.map((name) => ` compdef _photon_complete_direct ${name}`).join('\n')}
|
|
2011
|
+
fi
|
|
2012
|
+
|
|
2013
|
+
# Completion for the photon command itself
|
|
2014
|
+
_photon() {
|
|
2015
|
+
local curcontext="\$curcontext" state line
|
|
2016
|
+
_arguments -C \\
|
|
2017
|
+
"1: :->cmds" \\
|
|
2018
|
+
"*::arg:->args"
|
|
2019
|
+
case "\$state" in
|
|
2020
|
+
cmds)
|
|
2021
|
+
local -a builtins
|
|
2022
|
+
builtins=(
|
|
2023
|
+
'cli:Run a photon method'
|
|
2024
|
+
'use:Switch to a named instance'
|
|
2025
|
+
'instances:List instances of a photon'
|
|
2026
|
+
'set:Configure environment for a photon'
|
|
2027
|
+
'beam:Start the interactive UI'
|
|
2028
|
+
'serve:Start MCP stdio server'
|
|
2029
|
+
'list:List installed photons'
|
|
2030
|
+
'add:Install a photon'
|
|
2031
|
+
'remove:Uninstall a photon'
|
|
2032
|
+
'search:Search for photons'
|
|
2033
|
+
'info:Show photon details'
|
|
2034
|
+
'shell:Shell integration'
|
|
2035
|
+
'test:Run photon tests'
|
|
2036
|
+
'doctor:Check system health'
|
|
2037
|
+
)
|
|
2038
|
+
_describe 'command' builtins
|
|
2039
|
+
;;
|
|
2040
|
+
args)
|
|
2041
|
+
case \$line[1] in
|
|
2042
|
+
cli)
|
|
2043
|
+
local curcontext="\$curcontext" state line
|
|
2044
|
+
_arguments -C "1: :->photon_name" "*::arg:->method_args"
|
|
2045
|
+
case "\$state" in
|
|
2046
|
+
photon_name)
|
|
2047
|
+
if [[ -f "\$_photon_cache" ]]; then
|
|
2048
|
+
local -a photons
|
|
2049
|
+
photons=("\${(@f)$(grep "^photon:" "\$_photon_cache" | while IFS=: read -r _ name desc; do echo "\${name}:\${desc}"; done)}")
|
|
2050
|
+
_describe 'photon' photons
|
|
2051
|
+
fi
|
|
2052
|
+
;;
|
|
2053
|
+
method_args)
|
|
2054
|
+
words[1]="\$line[1]"
|
|
2055
|
+
_photon_complete_direct
|
|
2056
|
+
;;
|
|
2057
|
+
esac
|
|
2058
|
+
;;
|
|
2059
|
+
use|instances|set|info|serve)
|
|
2060
|
+
if [[ -f "\$_photon_cache" ]]; then
|
|
2061
|
+
local curcontext="\$curcontext" state line
|
|
2062
|
+
_arguments -C "1: :->photon_name" "*::arg:->instance"
|
|
2063
|
+
case "\$state" in
|
|
2064
|
+
photon_name)
|
|
2065
|
+
local -a photons
|
|
2066
|
+
photons=("\${(@f)$(grep "^photon:" "\$_photon_cache" | while IFS=: read -r _ name desc; do echo "\${name}:\${desc}"; done)}")
|
|
2067
|
+
_describe 'photon' photons
|
|
2068
|
+
;;
|
|
2069
|
+
instance)
|
|
2070
|
+
if [[ "\$line[-2]" == "use" ]]; then
|
|
2071
|
+
local -a instances
|
|
2072
|
+
instances=("\${(@f)$(grep "^instance:\${line[1]}:" "\$_photon_cache" | cut -d: -f3)}")
|
|
2073
|
+
[[ \${#instances} -gt 0 ]] && _describe 'instance' instances
|
|
2074
|
+
fi
|
|
2075
|
+
;;
|
|
2076
|
+
esac
|
|
2077
|
+
fi
|
|
2078
|
+
;;
|
|
2079
|
+
shell)
|
|
2080
|
+
local -a subcmds
|
|
2081
|
+
subcmds=('init:Set up shell integration' 'completions:Manage completion cache')
|
|
2082
|
+
_describe 'subcommand' subcmds
|
|
2083
|
+
;;
|
|
2084
|
+
esac
|
|
2085
|
+
;;
|
|
2086
|
+
esac
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
if (( $+functions[compdef] )); then
|
|
2090
|
+
compdef _photon photon
|
|
2091
|
+
fi`);
|
|
2092
|
+
}
|
|
2093
|
+
else if (shellType === 'bash') {
|
|
2094
|
+
const functions = photonNames
|
|
2095
|
+
.map((name) => `${name}() { photon cli ${name} "$@"; }`)
|
|
2096
|
+
.join('\n');
|
|
2097
|
+
console.log(`${marker}
|
|
2098
|
+
|
|
2099
|
+
# Shell functions for installed photons (direct invocation)
|
|
2100
|
+
${functions}
|
|
2101
|
+
|
|
2102
|
+
# Fallback for newly installed photons (before shell restart)
|
|
2103
|
+
command_not_found_handle() {
|
|
2104
|
+
if [ -f "$HOME/.photon/\$1.photon.ts" ] || [ -f "$HOME/.photon/\$1.photon.js" ]; then
|
|
2105
|
+
photon cli "$@"
|
|
2106
|
+
return $?
|
|
2107
|
+
fi
|
|
2108
|
+
echo "bash: \$1: command not found" >&2
|
|
2109
|
+
return 127
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
# Tab completion for photon methods, params, and instances
|
|
2113
|
+
_photon_cache="$HOME/.photon/cache/completions.cache"
|
|
2114
|
+
|
|
2115
|
+
_photon_complete_direct() {
|
|
2116
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
2117
|
+
local cmd="\${COMP_WORDS[0]}"
|
|
2118
|
+
COMPREPLY=()
|
|
2119
|
+
|
|
2120
|
+
if [[ ! -f "\$_photon_cache" ]]; then return; fi
|
|
2121
|
+
|
|
2122
|
+
if [[ \$COMP_CWORD -eq 1 ]]; then
|
|
2123
|
+
local methods
|
|
2124
|
+
methods="$(grep "^method:\${cmd}:" "\$_photon_cache" | cut -d: -f3)"
|
|
2125
|
+
COMPREPLY=($(compgen -W "\$methods" -- "\$cur"))
|
|
2126
|
+
elif [[ \$COMP_CWORD -eq 2 ]]; then
|
|
2127
|
+
local method="\${COMP_WORDS[1]}"
|
|
2128
|
+
local params
|
|
2129
|
+
params="$(grep "^param:\${cmd}:\${method}:" "\$_photon_cache" | cut -d: -f4 | sed 's/^/--/')"
|
|
2130
|
+
COMPREPLY=($(compgen -W "\$params" -- "\$cur"))
|
|
2131
|
+
fi
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
_photon_complete() {
|
|
2135
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
2136
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
2137
|
+
COMPREPLY=()
|
|
2138
|
+
|
|
2139
|
+
if [[ ! -f "\$_photon_cache" ]]; then return; fi
|
|
2140
|
+
|
|
2141
|
+
if [[ \$COMP_CWORD -eq 1 ]]; then
|
|
2142
|
+
COMPREPLY=($(compgen -W "cli use instances set beam serve list add remove search info shell test doctor" -- "\$cur"))
|
|
2143
|
+
elif [[ \$COMP_CWORD -eq 2 ]]; then
|
|
2144
|
+
case "\${COMP_WORDS[1]}" in
|
|
2145
|
+
cli|use|instances|set|info|serve)
|
|
2146
|
+
local photons
|
|
2147
|
+
photons="$(grep "^photon:" "\$_photon_cache" | cut -d: -f2)"
|
|
2148
|
+
COMPREPLY=($(compgen -W "\$photons" -- "\$cur"))
|
|
2149
|
+
;;
|
|
2150
|
+
shell)
|
|
2151
|
+
COMPREPLY=($(compgen -W "init completions" -- "\$cur"))
|
|
2152
|
+
;;
|
|
2153
|
+
esac
|
|
2154
|
+
elif [[ \$COMP_CWORD -eq 3 ]]; then
|
|
2155
|
+
case "\${COMP_WORDS[1]}" in
|
|
2156
|
+
cli)
|
|
2157
|
+
local methods
|
|
2158
|
+
methods="$(grep "^method:\${COMP_WORDS[2]}:" "\$_photon_cache" | cut -d: -f3)"
|
|
2159
|
+
COMPREPLY=($(compgen -W "\$methods" -- "\$cur"))
|
|
2160
|
+
;;
|
|
2161
|
+
use)
|
|
2162
|
+
local instances
|
|
2163
|
+
instances="$(grep "^instance:\${COMP_WORDS[2]}:" "\$_photon_cache" | cut -d: -f3)"
|
|
2164
|
+
COMPREPLY=($(compgen -W "\$instances" -- "\$cur"))
|
|
2165
|
+
;;
|
|
2166
|
+
esac
|
|
2167
|
+
elif [[ \$COMP_CWORD -ge 4 && "\${COMP_WORDS[1]}" == "cli" ]]; then
|
|
2168
|
+
local params
|
|
2169
|
+
params="$(grep "^param:\${COMP_WORDS[2]}:\${COMP_WORDS[3]}:" "\$_photon_cache" | cut -d: -f4 | sed 's/^/--/')"
|
|
2170
|
+
COMPREPLY=($(compgen -W "\$params" -- "\$cur"))
|
|
2171
|
+
fi
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
# Register completions
|
|
2175
|
+
${photonNames.map((name) => `complete -F _photon_complete_direct ${name}`).join('\n')}
|
|
2176
|
+
complete -F _photon_complete photon`);
|
|
2177
|
+
}
|
|
2178
|
+
else if (shellType === 'powershell') {
|
|
2179
|
+
// PowerShell functions and completion
|
|
2180
|
+
const functions = photonNames
|
|
2181
|
+
.map((name) => `function ${name} { photon cli ${name} @Args }`)
|
|
2182
|
+
.join('\n');
|
|
2183
|
+
const functionNames = photonNames.map((n) => `'${n}'`).join(', ');
|
|
2184
|
+
console.log(`${marker}
|
|
2185
|
+
|
|
2186
|
+
# Functions for installed photons (direct invocation)
|
|
2187
|
+
${functions}
|
|
2188
|
+
|
|
2189
|
+
# Fallback for newly installed photons (CommandNotFoundAction, PowerShell 7.4+)
|
|
2190
|
+
if ($PSVersionTable.PSVersion.Major -ge 7 -and $PSVersionTable.PSVersion.Minor -ge 4) {
|
|
2191
|
+
$ExecutionContext.InvokeCommand.CommandNotFoundAction = {
|
|
2192
|
+
param($Name, $EventArgs)
|
|
2193
|
+
$photonFile = Join-Path $HOME ".photon" "$Name.photon.ts"
|
|
2194
|
+
$photonFileJs = Join-Path $HOME ".photon" "$Name.photon.js"
|
|
2195
|
+
if ((Test-Path $photonFile) -or (Test-Path $photonFileJs)) {
|
|
2196
|
+
$EventArgs.CommandScriptBlock = { photon cli $Name @Args }.GetNewClosure()
|
|
2197
|
+
$EventArgs.StopSearch = $true
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
# Tab completion for photon methods, params, and instances
|
|
2203
|
+
$_photonCache = Join-Path $HOME ".photon" "cache" "completions.cache"
|
|
2204
|
+
|
|
2205
|
+
# Completion for direct photon commands
|
|
2206
|
+
${photonNames
|
|
2207
|
+
.map((name) => `Register-ArgumentCompleter -CommandName ${name} -ScriptBlock {
|
|
2208
|
+
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
2209
|
+
if (-not (Test-Path $_photonCache)) { return }
|
|
2210
|
+
$pos = $commandAst.CommandElements.Count
|
|
2211
|
+
if ($pos -le 1) {
|
|
2212
|
+
# Complete method names
|
|
2213
|
+
Get-Content $_photonCache | Where-Object { $_ -match "^method:\${commandName}:" } | ForEach-Object {
|
|
2214
|
+
$parts = $_ -split ':', 4
|
|
2215
|
+
[System.Management.Automation.CompletionResult]::new($parts[2], $parts[2], 'ParameterValue', ($parts[3] ?? $parts[2]))
|
|
2216
|
+
} | Where-Object { $_.CompletionText -like "$wordToComplete*" }
|
|
2217
|
+
} elseif ($pos -le 2) {
|
|
2218
|
+
# Complete parameter names
|
|
2219
|
+
$method = $commandAst.CommandElements[1].Value
|
|
2220
|
+
Get-Content $_photonCache | Where-Object { $_ -match "^param:\${commandName}:\${method}:" } | ForEach-Object {
|
|
2221
|
+
$parts = $_ -split ':', 6
|
|
2222
|
+
$paramName = "--$($parts[3])"
|
|
2223
|
+
[System.Management.Automation.CompletionResult]::new($paramName, $paramName, 'ParameterName', "$($parts[4]) parameter")
|
|
2224
|
+
} | Where-Object { $_.CompletionText -like "$wordToComplete*" }
|
|
2225
|
+
}
|
|
2226
|
+
}`)
|
|
2227
|
+
.join('\n')}
|
|
2228
|
+
|
|
2229
|
+
# Completion for the photon command itself
|
|
2230
|
+
Register-ArgumentCompleter -CommandName photon -ScriptBlock {
|
|
2231
|
+
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
2232
|
+
$pos = $commandAst.CommandElements.Count
|
|
2233
|
+
if ($pos -le 1) {
|
|
2234
|
+
@('cli','use','instances','set','beam','serve','list','add','remove','search','info','shell','test','doctor') |
|
|
2235
|
+
Where-Object { $_ -like "$wordToComplete*" } |
|
|
2236
|
+
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
2237
|
+
} elseif ($pos -le 2) {
|
|
2238
|
+
$sub = $commandAst.CommandElements[1].Value
|
|
2239
|
+
switch ($sub) {
|
|
2240
|
+
{ $_ -in 'cli','use','instances','set','info','serve' } {
|
|
2241
|
+
if (Test-Path $_photonCache) {
|
|
2242
|
+
Get-Content $_photonCache | Where-Object { $_ -match "^photon:" } | ForEach-Object {
|
|
2243
|
+
$parts = $_ -split ':', 3
|
|
2244
|
+
[System.Management.Automation.CompletionResult]::new($parts[1], $parts[1], 'ParameterValue', ($parts[2] ?? $parts[1]))
|
|
2245
|
+
} | Where-Object { $_.CompletionText -like "$wordToComplete*" }
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
'shell' {
|
|
2249
|
+
@('init','completions') | Where-Object { $_ -like "$wordToComplete*" } |
|
|
2250
|
+
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
} elseif ($pos -le 3) {
|
|
2254
|
+
$sub = $commandAst.CommandElements[1].Value
|
|
2255
|
+
$photonName = $commandAst.CommandElements[2].Value
|
|
2256
|
+
if ($sub -eq 'cli' -and (Test-Path $_photonCache)) {
|
|
2257
|
+
Get-Content $_photonCache | Where-Object { $_ -match "^method:\${photonName}:" } | ForEach-Object {
|
|
2258
|
+
$parts = $_ -split ':', 4
|
|
2259
|
+
[System.Management.Automation.CompletionResult]::new($parts[2], $parts[2], 'ParameterValue', ($parts[3] ?? $parts[2]))
|
|
2260
|
+
} | Where-Object { $_.CompletionText -like "$wordToComplete*" }
|
|
2261
|
+
} elseif ($sub -eq 'use' -and (Test-Path $_photonCache)) {
|
|
2262
|
+
Get-Content $_photonCache | Where-Object { $_ -match "^instance:\${photonName}:" } | ForEach-Object {
|
|
2263
|
+
$parts = $_ -split ':', 3
|
|
2264
|
+
[System.Management.Automation.CompletionResult]::new($parts[2], $parts[2], 'ParameterValue', $parts[2])
|
|
2265
|
+
} | Where-Object { $_.CompletionText -like "$wordToComplete*" }
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
}`);
|
|
2269
|
+
}
|
|
2270
|
+
// Silently generate cache on first hook (non-blocking)
|
|
2271
|
+
const { CACHE_FILE } = await import('./shell-completions.js');
|
|
2272
|
+
try {
|
|
2273
|
+
await fs.access(CACHE_FILE);
|
|
2274
|
+
}
|
|
2275
|
+
catch {
|
|
2276
|
+
// Cache doesn't exist yet — generate it
|
|
2277
|
+
const { generateCompletionCache } = await import('./shell-completions.js');
|
|
2278
|
+
await generateCompletionCache();
|
|
2279
|
+
}
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
// Interactive mode → install into rc file
|
|
2283
|
+
try {
|
|
2284
|
+
// Ensure profile directory exists (PowerShell profile dir may not)
|
|
2285
|
+
const rcDir = path.dirname(rcFile);
|
|
2286
|
+
await fs.mkdir(rcDir, { recursive: true });
|
|
2287
|
+
let rcContent = '';
|
|
2288
|
+
try {
|
|
2289
|
+
rcContent = await fs.readFile(rcFile, 'utf-8');
|
|
2290
|
+
}
|
|
2291
|
+
catch {
|
|
2292
|
+
// rc file doesn't exist, we'll create it
|
|
2293
|
+
}
|
|
2294
|
+
if (rcContent.includes(marker) || rcContent.includes(evalLine)) {
|
|
2295
|
+
printInfo(`Shell integration already installed in ${rcFile}`);
|
|
2296
|
+
if (shellType === 'powershell') {
|
|
2297
|
+
console.log(` Restart PowerShell or run: . $PROFILE`);
|
|
2298
|
+
}
|
|
2299
|
+
else {
|
|
2300
|
+
console.log(` Restart your shell or run: source ${rcFile}`);
|
|
2301
|
+
}
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
const block = `\n${marker}\n${evalLine}\n`;
|
|
2305
|
+
await fs.appendFile(rcFile, block);
|
|
2306
|
+
// Generate completions cache
|
|
2307
|
+
const { generateCompletionCache } = await import('./shell-completions.js');
|
|
2308
|
+
await generateCompletionCache();
|
|
2309
|
+
printSuccess(`Installed shell integration into ${rcFile}`);
|
|
2310
|
+
if (shellType === 'powershell') {
|
|
2311
|
+
console.log(` Restart PowerShell or run: . $PROFILE`);
|
|
2312
|
+
}
|
|
2313
|
+
else {
|
|
2314
|
+
console.log(` Restart your shell or run: source ${rcFile}`);
|
|
2315
|
+
}
|
|
2316
|
+
console.log('');
|
|
2317
|
+
console.log(` Then type any photon name directly:`);
|
|
2318
|
+
console.log(` list get → photon cli list get`);
|
|
2319
|
+
console.log(` list add "Milk" → photon cli list add "Milk"`);
|
|
2320
|
+
console.log('');
|
|
2321
|
+
console.log(' Tab completion is enabled for:');
|
|
2322
|
+
console.log(' Photon names, methods, parameters, and instances.');
|
|
2323
|
+
}
|
|
2324
|
+
catch (error) {
|
|
2325
|
+
printError(`Failed to update ${rcFile}: ${getErrorMessage(error)}`);
|
|
2326
|
+
console.log(` Add this line manually to your shell profile:`);
|
|
2327
|
+
console.log(` ${evalLine}`);
|
|
2328
|
+
process.exit(1);
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
shell
|
|
2332
|
+
.command('completions')
|
|
2333
|
+
.option('--generate', 'Regenerate the completions cache')
|
|
2334
|
+
.description('Manage shell completion cache')
|
|
2335
|
+
.action(async (options) => {
|
|
2336
|
+
const { generateCompletionCache, CACHE_FILE } = await import('./shell-completions.js');
|
|
2337
|
+
if (options.generate) {
|
|
2338
|
+
await generateCompletionCache();
|
|
2339
|
+
printSuccess(`Completions cache updated: ${CACHE_FILE}`);
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
// Default: show cache status
|
|
2343
|
+
try {
|
|
2344
|
+
const stat = await fs.stat(CACHE_FILE);
|
|
2345
|
+
const age = Date.now() - stat.mtimeMs;
|
|
2346
|
+
const ageStr = age < 60_000
|
|
2347
|
+
? 'just now'
|
|
2348
|
+
: age < 3_600_000
|
|
2349
|
+
? `${Math.floor(age / 60_000)}m ago`
|
|
2350
|
+
: `${Math.floor(age / 3_600_000)}h ago`;
|
|
2351
|
+
printInfo(`Cache: ${CACHE_FILE}`);
|
|
2352
|
+
console.log(` Last updated: ${ageStr}`);
|
|
2353
|
+
console.log(` Run \`photon shell completions --generate\` to refresh`);
|
|
2354
|
+
}
|
|
2355
|
+
catch {
|
|
2356
|
+
printInfo('No completions cache found.');
|
|
2357
|
+
console.log(' Run `photon shell completions --generate` to create one.');
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
// Set command: configure environment for photons (primitive params without defaults)
|
|
2361
|
+
program
|
|
2362
|
+
.command('set')
|
|
2363
|
+
.argument('<photon>', 'Photon name')
|
|
2364
|
+
.argument('[args...]', 'Environment values (name=value pairs)')
|
|
2365
|
+
.description('Configure environment for a photon (params without defaults)')
|
|
2366
|
+
.action(async (photonName, args) => {
|
|
2367
|
+
try {
|
|
2368
|
+
const workingDir = program.opts().dir || DEFAULT_WORKING_DIR;
|
|
2369
|
+
// Resolve photon path
|
|
2370
|
+
const filePath = await resolvePhotonPathWithBundled(photonName, workingDir);
|
|
2371
|
+
if (!filePath) {
|
|
2372
|
+
printError(`Photon not found: ${photonName}`);
|
|
2373
|
+
process.exit(1);
|
|
2374
|
+
}
|
|
2375
|
+
// Extract constructor params and filter env params
|
|
2376
|
+
const allParams = await extractConstructorParams(filePath);
|
|
2377
|
+
const { getEnvParams, EnvStore } = await import('./context-store.js');
|
|
2378
|
+
const envParams = getEnvParams(allParams);
|
|
2379
|
+
if (envParams.length === 0) {
|
|
2380
|
+
printInfo(`${photonName} has no environment parameters.`);
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
const store = new EnvStore();
|
|
2384
|
+
// Parse name=value pairs from args
|
|
2385
|
+
const values = {};
|
|
2386
|
+
const paramNames = new Set(envParams.map((p) => p.name));
|
|
2387
|
+
for (const arg of args) {
|
|
2388
|
+
const eqIdx = arg.indexOf('=');
|
|
2389
|
+
if (eqIdx > 0) {
|
|
2390
|
+
const key = arg.slice(0, eqIdx);
|
|
2391
|
+
const val = arg.slice(eqIdx + 1);
|
|
2392
|
+
if (paramNames.has(key)) {
|
|
2393
|
+
values[key] = val;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
else if (envParams.length === 1) {
|
|
2397
|
+
// Single env param: positional value
|
|
2398
|
+
values[envParams[0].name] = arg;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
// Find params that still need values
|
|
2402
|
+
const remaining = envParams.filter((p) => !(p.name in values));
|
|
2403
|
+
if (remaining.length > 0) {
|
|
2404
|
+
// Interactive mode for remaining params
|
|
2405
|
+
cliHeading(`${photonName} — Environment`);
|
|
2406
|
+
cliSpacer();
|
|
2407
|
+
const masked = store.getMasked(photonName);
|
|
2408
|
+
for (const param of remaining) {
|
|
2409
|
+
const currentDisplay = masked[param.name] ? `Current: ${masked[param.name]}` : 'Not set';
|
|
2410
|
+
const answer = await promptText(` ${param.name} (required)\n ${currentDisplay}\n > `);
|
|
2411
|
+
if (answer.trim() !== '') {
|
|
2412
|
+
values[param.name] = answer.trim();
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (Object.keys(values).length > 0) {
|
|
2417
|
+
store.write(photonName, values);
|
|
2418
|
+
const summary = Object.keys(values).join(', ');
|
|
2419
|
+
printSuccess(`Environment saved: ${summary}`);
|
|
2420
|
+
}
|
|
2421
|
+
else {
|
|
2422
|
+
printInfo('No changes.');
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
catch (error) {
|
|
2426
|
+
printError(getErrorMessage(error));
|
|
2427
|
+
process.exit(1);
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
1827
2430
|
// Alias commands: create CLI shortcuts for photons
|
|
1828
2431
|
program
|
|
1829
2432
|
.command('alias', { hidden: true })
|
|
@@ -1908,6 +2511,10 @@ const RESERVED_COMMANDS = [
|
|
|
1908
2511
|
'doctor',
|
|
1909
2512
|
'clear-cache',
|
|
1910
2513
|
'clean',
|
|
2514
|
+
// Instance/env
|
|
2515
|
+
'use',
|
|
2516
|
+
'instances',
|
|
2517
|
+
'set',
|
|
1911
2518
|
// Aliases
|
|
1912
2519
|
'cli',
|
|
1913
2520
|
'alias',
|
|
@@ -1922,6 +2529,7 @@ const RESERVED_COMMANDS = [
|
|
|
1922
2529
|
'search',
|
|
1923
2530
|
'maker',
|
|
1924
2531
|
'host',
|
|
2532
|
+
'shell',
|
|
1925
2533
|
'diagram',
|
|
1926
2534
|
'diagrams',
|
|
1927
2535
|
'enable',
|
|
@@ -1956,6 +2564,9 @@ const knownCommands = [
|
|
|
1956
2564
|
'clear-cache',
|
|
1957
2565
|
'clean',
|
|
1958
2566
|
'doctor',
|
|
2567
|
+
'use',
|
|
2568
|
+
'instances',
|
|
2569
|
+
'set',
|
|
1959
2570
|
'cli',
|
|
1960
2571
|
'alias',
|
|
1961
2572
|
'unalias',
|
|
@@ -1965,12 +2576,14 @@ const knownCommands = [
|
|
|
1965
2576
|
'marketplace',
|
|
1966
2577
|
'maker',
|
|
1967
2578
|
'host',
|
|
2579
|
+
'shell',
|
|
1968
2580
|
'diagram',
|
|
1969
2581
|
'diagrams',
|
|
1970
2582
|
];
|
|
1971
2583
|
const knownSubcommands = {
|
|
1972
2584
|
marketplace: ['list', 'add', 'remove', 'enable', 'disable'],
|
|
1973
2585
|
maker: ['new', 'validate', 'sync', 'init'],
|
|
2586
|
+
shell: ['init', 'completions'],
|
|
1974
2587
|
};
|
|
1975
2588
|
/**
|
|
1976
2589
|
* Calculate Levenshtein distance between two strings
|
|
@@ -2048,9 +2661,10 @@ program.on('command:*', async (operands) => {
|
|
|
2048
2661
|
// This enables: `photon lg-remote volume +5` instead of `photon cli lg-remote volume +5`
|
|
2049
2662
|
function preprocessArgs() {
|
|
2050
2663
|
const args = process.argv.slice(2);
|
|
2051
|
-
// No args -
|
|
2664
|
+
// No args - launch Beam (the primary interface)
|
|
2665
|
+
// Use `photon -h` or `photon --help` for help
|
|
2052
2666
|
if (args.length === 0) {
|
|
2053
|
-
return [...process.argv, 'beam'
|
|
2667
|
+
return [...process.argv, 'beam'];
|
|
2054
2668
|
}
|
|
2055
2669
|
// Find the first non-flag argument (skip values of flags that take a parameter)
|
|
2056
2670
|
const flagsWithValues = ['--dir', '--log-level'];
|
|
@@ -2068,8 +2682,8 @@ function preprocessArgs() {
|
|
|
2068
2682
|
if (args.some((a) => a === '--help' || a === '-h' || a === '--version' || a === '-V')) {
|
|
2069
2683
|
return process.argv;
|
|
2070
2684
|
}
|
|
2071
|
-
// Otherwise
|
|
2072
|
-
return [...process.argv, 'beam'
|
|
2685
|
+
// Otherwise launch Beam (e.g., photon --dir=.)
|
|
2686
|
+
return [...process.argv, 'beam'];
|
|
2073
2687
|
}
|
|
2074
2688
|
const firstArg = args[firstArgIndex];
|
|
2075
2689
|
// If first arg is a reserved command, let commander handle normally
|