@f5-sales-demo/xcsh 19.65.0 → 19.66.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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "19.
|
|
4
|
+
"version": "19.66.1",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"xcsh": "src/cli.ts"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-branding-index && bun run generate-terraform-index && bun run generate-console-catalog && bun run generate-console-field-metadata && test -f src/internal-urls/api-spec-index.generated.ts && bun --cwd=../stats scripts/generate-client-bundle.ts --generate && bun --cwd=../natives run embed:native && bun build --compile --define PI_COMPILED=true --external mupdf --root ../.. ./src/cli.ts --outfile dist/xcsh && bun --cwd=../natives run embed:native --reset && bun --cwd=../stats scripts/generate-client-bundle.ts --reset",
|
|
34
|
+
"build": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-branding-index && bun run generate-terraform-index && bun run generate-console-catalog && bun run generate-console-field-metadata && test -f src/internal-urls/api-spec-index.generated.ts && bun --cwd=../stats scripts/generate-client-bundle.ts --generate && bun --cwd=../office-pane scripts/generate-client-bundle.ts --generate && bun --cwd=../natives run embed:native && bun build --compile --define PI_COMPILED=true --external mupdf --root ../.. ./src/cli.ts --outfile dist/xcsh && bun --cwd=../natives run embed:native --reset && bun --cwd=../stats scripts/generate-client-bundle.ts --reset && bun --cwd=../office-pane scripts/generate-client-bundle.ts --reset",
|
|
35
35
|
"check": "biome check . && bun run format-prompts -- --check && bun run check:types",
|
|
36
36
|
"check:types": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-branding-index && bun run generate-terraform-index && tsgo -p tsconfig.json --noEmit",
|
|
37
37
|
"lint": "biome lint .",
|
|
@@ -56,13 +56,13 @@
|
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
58
58
|
"@mozilla/readability": "^0.6",
|
|
59
|
-
"@f5-sales-demo/xcsh-stats": "19.
|
|
60
|
-
"@f5-sales-demo/pi-agent-core": "19.
|
|
61
|
-
"@f5-sales-demo/pi-ai": "19.
|
|
62
|
-
"@f5-sales-demo/pi-natives": "19.
|
|
63
|
-
"@f5-sales-demo/pi-resource-management": "19.
|
|
64
|
-
"@f5-sales-demo/pi-tui": "19.
|
|
65
|
-
"@f5-sales-demo/pi-utils": "19.
|
|
59
|
+
"@f5-sales-demo/xcsh-stats": "19.66.1",
|
|
60
|
+
"@f5-sales-demo/pi-agent-core": "19.66.1",
|
|
61
|
+
"@f5-sales-demo/pi-ai": "19.66.1",
|
|
62
|
+
"@f5-sales-demo/pi-natives": "19.66.1",
|
|
63
|
+
"@f5-sales-demo/pi-resource-management": "19.66.1",
|
|
64
|
+
"@f5-sales-demo/pi-tui": "19.66.1",
|
|
65
|
+
"@f5-sales-demo/pi-utils": "19.66.1",
|
|
66
66
|
"@sinclair/typebox": "^0.34",
|
|
67
67
|
"@xterm/headless": "^6.0",
|
|
68
68
|
"ajv": "^8.20",
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static HTTPS listener for the embedded Office task pane.
|
|
3
|
+
*
|
|
4
|
+
* Serves the add-in's page shell + JS bundle + unified manifest + ribbon/app
|
|
5
|
+
* icons over a FIXED `https://127-0-0-1.local-ip.sh:8444` origin — the exact URL
|
|
6
|
+
* the manifest's `code.page` and ribbon icon URLs point at. TLS terminates with
|
|
7
|
+
* the SAME publicly-trusted `*.local-ip.sh` cert the `wss` extension bridge uses
|
|
8
|
+
* (via {@link resolveBridgeTls}), so Office's WebView loads the page with real
|
|
9
|
+
* TLS verification and no local trust / MDM step.
|
|
10
|
+
*
|
|
11
|
+
* This listener is SEPARATE from the extension bridge and serves ONLY static GET
|
|
12
|
+
* requests (no WebSocket). It never auto-starts — it is launched exclusively by
|
|
13
|
+
* `xcsh office serve`, keeping blast radius zero.
|
|
14
|
+
*
|
|
15
|
+
* Compiled vs dev (Option A — the pane is a build-time embedded asset of the
|
|
16
|
+
* binary, not a published library):
|
|
17
|
+
* - COMPILED: the base64 tar.gz baked into `office-pane.generated.txt` is
|
|
18
|
+
* extracted (once, path-traversal-guarded) to `os.tmpdir()/xcsh-office-pane/<hash>`.
|
|
19
|
+
* - DEV: assets are read straight from `packages/office-pane/dist` via a
|
|
20
|
+
* filesystem path — never a module import, so no dependency on the private
|
|
21
|
+
* office-pane package is introduced.
|
|
22
|
+
*/
|
|
23
|
+
import * as fs from "node:fs/promises";
|
|
24
|
+
import * as os from "node:os";
|
|
25
|
+
import * as path from "node:path";
|
|
26
|
+
import { LOCALIP_HOST, resolveBridgeTls } from "./bridge-cert";
|
|
27
|
+
import embeddedPaneArchiveTxt from "./office-pane.generated.txt";
|
|
28
|
+
|
|
29
|
+
/** Fixed listener port — must match the manifest page + ribbon icon URLs. */
|
|
30
|
+
export const OFFICE_PANE_PORT = 8444;
|
|
31
|
+
/** Bind loopback only; the local-ip.sh SAN host resolves here for the WebView. */
|
|
32
|
+
export const OFFICE_PANE_HOSTNAME = "127.0.0.1";
|
|
33
|
+
/** The trusted-origin base URL the task pane is reachable at. */
|
|
34
|
+
export const OFFICE_PANE_URL = `https://${LOCALIP_HOST}:${OFFICE_PANE_PORT}`;
|
|
35
|
+
/** The task-pane page URL (the manifest `code.page`). */
|
|
36
|
+
export const OFFICE_PANE_TASKPANE_URL = `${OFFICE_PANE_URL}/taskpane.html`;
|
|
37
|
+
|
|
38
|
+
const IS_BUN_COMPILED =
|
|
39
|
+
Bun.env.PI_COMPILED ||
|
|
40
|
+
import.meta.url.includes("$bunfs") ||
|
|
41
|
+
import.meta.url.includes("~BUN") ||
|
|
42
|
+
import.meta.url.includes("%7EBUN");
|
|
43
|
+
|
|
44
|
+
// Dev-mode assets: packages/coding-agent/src/browser → packages/office-pane/dist.
|
|
45
|
+
const DEV_DIST_DIR = path.resolve(import.meta.dir, "..", "..", "..", "office-pane", "dist");
|
|
46
|
+
const COMPILED_DIR_ROOT = path.join(os.tmpdir(), "xcsh-office-pane");
|
|
47
|
+
|
|
48
|
+
const getEmbeddedArchive = (() => {
|
|
49
|
+
const txt = embeddedPaneArchiveTxt.replaceAll(/[\s\r\n]/g, "").trim();
|
|
50
|
+
if (!txt) return null;
|
|
51
|
+
return () => Buffer.from(txt, "base64");
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
let compiledDirPromise: Promise<string> | null = null;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sanitize an archive-relative path, rejecting anything that would escape the
|
|
58
|
+
* extraction/serve root (`..`, absolute paths, empty/`.`). Returns the
|
|
59
|
+
* normalized forward-slash path, or `null` when the input is unsafe.
|
|
60
|
+
*/
|
|
61
|
+
export function sanitizeArchivePath(archivePath: string): string | null {
|
|
62
|
+
const normalized = archivePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
63
|
+
if (!normalized || normalized === ".") return null;
|
|
64
|
+
if (normalized.includes("..") || path.isAbsolute(normalized)) return null;
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function extractEmbeddedArchive(archiveBytes: Buffer, outputDir: string): Promise<void> {
|
|
69
|
+
const archive = new Bun.Archive(archiveBytes);
|
|
70
|
+
const files = await archive.files();
|
|
71
|
+
const extractRoot = path.resolve(outputDir);
|
|
72
|
+
|
|
73
|
+
for (const [archivePath, file] of files) {
|
|
74
|
+
const sanitizedPath = sanitizeArchivePath(archivePath);
|
|
75
|
+
if (!sanitizedPath) continue;
|
|
76
|
+
const destinationPath = path.resolve(extractRoot, sanitizedPath);
|
|
77
|
+
if (!destinationPath.startsWith(extractRoot + path.sep)) {
|
|
78
|
+
throw new Error(`Archive entry escapes extraction directory: ${archivePath}`);
|
|
79
|
+
}
|
|
80
|
+
await Bun.write(destinationPath, file);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the directory the assets are served from: the extracted embedded
|
|
86
|
+
* bundle in a compiled binary, else `packages/office-pane/dist` in dev.
|
|
87
|
+
*/
|
|
88
|
+
export async function getOfficePaneDir(): Promise<string> {
|
|
89
|
+
if (!IS_BUN_COMPILED) return DEV_DIST_DIR;
|
|
90
|
+
if (compiledDirPromise) return compiledDirPromise;
|
|
91
|
+
|
|
92
|
+
const archiveBytes = getEmbeddedArchive?.();
|
|
93
|
+
if (!archiveBytes) {
|
|
94
|
+
throw new Error("Compiled office-pane bundle missing. Rebuild the binary with the embedded office-pane assets.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
compiledDirPromise = (async () => {
|
|
98
|
+
const bundleHash = Bun.hash(archiveBytes).toString(16);
|
|
99
|
+
const outputDir = path.join(COMPILED_DIR_ROOT, bundleHash);
|
|
100
|
+
const markerPath = path.join(outputDir, "taskpane.html");
|
|
101
|
+
try {
|
|
102
|
+
if ((await fs.stat(markerPath)).isFile()) return outputDir;
|
|
103
|
+
} catch {}
|
|
104
|
+
|
|
105
|
+
await fs.rm(outputDir, { recursive: true, force: true });
|
|
106
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
107
|
+
await extractEmbeddedArchive(archiveBytes, outputDir);
|
|
108
|
+
return outputDir;
|
|
109
|
+
})();
|
|
110
|
+
|
|
111
|
+
return compiledDirPromise;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Pure request handler: map a URL pathname to a file under `dir` and return it
|
|
116
|
+
* with the content-type inferred from its extension, or a 404. `/` maps to
|
|
117
|
+
* `taskpane.html`. Path-traversal is rejected before any filesystem access.
|
|
118
|
+
*/
|
|
119
|
+
export async function handleAssetRequest(pathname: string, dir: string): Promise<Response> {
|
|
120
|
+
const requested = pathname === "/" ? "taskpane.html" : pathname.replace(/^\/+/, "");
|
|
121
|
+
const safe = sanitizeArchivePath(requested);
|
|
122
|
+
if (!safe) return new Response("Not Found", { status: 404 });
|
|
123
|
+
|
|
124
|
+
const root = path.resolve(dir);
|
|
125
|
+
const fullPath = path.resolve(root, safe);
|
|
126
|
+
if (fullPath !== root && !fullPath.startsWith(root + path.sep)) {
|
|
127
|
+
return new Response("Not Found", { status: 404 });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const file = Bun.file(fullPath);
|
|
131
|
+
if (await file.exists()) return new Response(file);
|
|
132
|
+
return new Response("Not Found", { status: 404 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Read the embedded/dev manifest.json as text (for `xcsh office manifest`). */
|
|
136
|
+
export async function readManifest(): Promise<string> {
|
|
137
|
+
const dir = await getOfficePaneDir();
|
|
138
|
+
return Bun.file(path.join(dir, "manifest.json")).text();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface OfficePaneServer {
|
|
142
|
+
port: number;
|
|
143
|
+
url: string;
|
|
144
|
+
taskpaneUrl: string;
|
|
145
|
+
/** true when a publicly-trusted `*.local-ip.sh` cert is in use. */
|
|
146
|
+
trusted: boolean;
|
|
147
|
+
stop: () => void;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Start the fixed :8444 HTTPS listener serving the embedded/dev assets. Resolves
|
|
152
|
+
* TLS via {@link resolveBridgeTls} (the shared local-ip.sh cert). Serves only GET
|
|
153
|
+
* (405 otherwise); unknown paths return 404.
|
|
154
|
+
*/
|
|
155
|
+
export async function startOfficePaneServer(port = OFFICE_PANE_PORT): Promise<OfficePaneServer> {
|
|
156
|
+
const dir = await getOfficePaneDir();
|
|
157
|
+
const tls = await resolveBridgeTls();
|
|
158
|
+
|
|
159
|
+
const server = Bun.serve({
|
|
160
|
+
port,
|
|
161
|
+
hostname: OFFICE_PANE_HOSTNAME,
|
|
162
|
+
tls,
|
|
163
|
+
async fetch(req) {
|
|
164
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
165
|
+
return new Response("Method Not Allowed", { status: 405 });
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const url = new URL(req.url);
|
|
169
|
+
return await handleAssetRequest(url.pathname, dir);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return new Response(error instanceof Error ? error.message : "Internal Server Error", { status: 500 });
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
port: server.port ?? port,
|
|
178
|
+
url: OFFICE_PANE_URL,
|
|
179
|
+
taskpaneUrl: OFFICE_PANE_TASKPANE_URL,
|
|
180
|
+
trusted: tls !== undefined,
|
|
181
|
+
stop: () => server.stop(),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `xcsh office` command handlers.
|
|
3
|
+
*
|
|
4
|
+
* Drives the embedded Office task pane: start its fixed :8444 HTTPS listener
|
|
5
|
+
* (`serve`), emit the unified manifest (`manifest`), or sideload it into a
|
|
6
|
+
* desktop Office app (`sideload`). The serving/embed logic lives in
|
|
7
|
+
* `../browser/office-pane-server`; this module is the thin, testable CLI seam
|
|
8
|
+
* mirroring `stats-cli.ts` / `chrome-cli.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import * as fs from "node:fs/promises";
|
|
12
|
+
import * as os from "node:os";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { readManifest, startOfficePaneServer } from "../browser/office-pane-server";
|
|
15
|
+
|
|
16
|
+
/** The subcommands `xcsh office` accepts (also the Args `options` constraint). */
|
|
17
|
+
export const OFFICE_ACTIONS = ["serve", "manifest", "sideload"] as const;
|
|
18
|
+
export type OfficeAction = (typeof OFFICE_ACTIONS)[number];
|
|
19
|
+
|
|
20
|
+
/** Desktop Office apps a sideload can target. */
|
|
21
|
+
export const OFFICE_APPS = ["excel", "powerpoint", "word"] as const;
|
|
22
|
+
export type OfficeApp = (typeof OFFICE_APPS)[number];
|
|
23
|
+
|
|
24
|
+
export interface OfficeCommandArgs {
|
|
25
|
+
action: OfficeAction;
|
|
26
|
+
/** Target app for `sideload` (defaults to excel). */
|
|
27
|
+
app?: OfficeApp;
|
|
28
|
+
/** Optional output path for `manifest`; when omitted, print to stdout. */
|
|
29
|
+
out?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the manifest text and optionally write it to `outPath`. Returns the
|
|
34
|
+
* manifest JSON string. Exposed for unit testing.
|
|
35
|
+
*/
|
|
36
|
+
export async function writeManifest(outPath?: string): Promise<string> {
|
|
37
|
+
const text = await readManifest();
|
|
38
|
+
if (outPath) {
|
|
39
|
+
await Bun.write(outPath, text);
|
|
40
|
+
}
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Start the :8444 listener, print the task-pane URL, and block until killed. */
|
|
45
|
+
async function runServe(): Promise<void> {
|
|
46
|
+
const server = await startOfficePaneServer();
|
|
47
|
+
console.log(`Serving the xcsh Office task pane at ${server.taskpaneUrl}`);
|
|
48
|
+
if (!server.trusted) {
|
|
49
|
+
console.warn(
|
|
50
|
+
"Warning: a publicly-trusted local-ip.sh cert could not be provisioned; using a self-signed fallback. " +
|
|
51
|
+
"Office's WebView may refuse to load the page until the cert is trusted.",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
console.log("Press Ctrl+C to stop.");
|
|
55
|
+
// Bun.serve holds the event loop open; block run() so the process stays alive.
|
|
56
|
+
await new Promise<never>(() => {});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Emit the manifest to a temp file and run the Office sideload (best-effort). */
|
|
60
|
+
async function runSideload(app: OfficeApp): Promise<void> {
|
|
61
|
+
const text = await readManifest();
|
|
62
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "xcsh-office-sideload-"));
|
|
63
|
+
const manifestPath = path.join(dir, "manifest.json");
|
|
64
|
+
await Bun.write(manifestPath, text);
|
|
65
|
+
console.log(`Wrote manifest to ${manifestPath}`);
|
|
66
|
+
console.log(`Sideloading into ${app} (requires the office-addin-debugging / atk tool on PATH)...`);
|
|
67
|
+
|
|
68
|
+
const result = spawnSync("office-addin-debugging", ["start", manifestPath, "desktop", "--app", app], {
|
|
69
|
+
stdio: "inherit",
|
|
70
|
+
});
|
|
71
|
+
if (result.error) {
|
|
72
|
+
const code = (result.error as NodeJS.ErrnoException).code;
|
|
73
|
+
if (code === "ENOENT") {
|
|
74
|
+
console.error(
|
|
75
|
+
"office-addin-debugging was not found on PATH. Install it (e.g. `npm i -g office-addin-debugging` " +
|
|
76
|
+
"or `npm i -g @microsoft/m365agentstoolkit-cli` for `atk`), then run:\n" +
|
|
77
|
+
` office-addin-debugging start ${manifestPath} desktop --app ${app}`,
|
|
78
|
+
);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
throw result.error;
|
|
82
|
+
}
|
|
83
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
84
|
+
console.error(`office-addin-debugging exited with status ${result.status}.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Dispatch an `xcsh office <action>` invocation. */
|
|
89
|
+
export async function runOfficeCommand(args: OfficeCommandArgs): Promise<void> {
|
|
90
|
+
switch (args.action) {
|
|
91
|
+
case "serve":
|
|
92
|
+
await runServe();
|
|
93
|
+
return;
|
|
94
|
+
case "manifest": {
|
|
95
|
+
const text = await writeManifest(args.out);
|
|
96
|
+
if (args.out) {
|
|
97
|
+
console.log(`Wrote manifest to ${args.out}`);
|
|
98
|
+
} else {
|
|
99
|
+
console.log(text);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
case "sideload":
|
|
104
|
+
await runSideload(args.app ?? "excel");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -58,6 +58,7 @@ const commands: CommandEntry[] = [
|
|
|
58
58
|
{ name: "read", load: () => import("./commands/read").then(m => m.default) },
|
|
59
59
|
{ name: "jupyter", load: () => import("./commands/jupyter").then(m => m.default) },
|
|
60
60
|
{ name: "manager", load: () => import("./commands/manager").then(m => m.default) },
|
|
61
|
+
{ name: "office", load: () => import("./commands/office").then(m => m.default) },
|
|
61
62
|
{ name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
|
|
62
63
|
{ name: "setup", load: () => import("./commands/setup").then(m => m.default) },
|
|
63
64
|
{ name: "shell", load: () => import("./commands/shell").then(m => m.default) },
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve, print, or sideload the embedded xcsh Office task pane.
|
|
3
|
+
*/
|
|
4
|
+
import { Args, Command, Flags } from "@f5-sales-demo/pi-utils/cli";
|
|
5
|
+
import { OFFICE_ACTIONS, OFFICE_APPS, type OfficeAction, type OfficeApp, runOfficeCommand } from "../cli/office-cli";
|
|
6
|
+
|
|
7
|
+
export default class Office extends Command {
|
|
8
|
+
static description = "Serve, print, or sideload the embedded xcsh Office task pane";
|
|
9
|
+
|
|
10
|
+
static args = {
|
|
11
|
+
action: Args.string({
|
|
12
|
+
description: "serve | manifest | sideload",
|
|
13
|
+
required: false,
|
|
14
|
+
options: OFFICE_ACTIONS,
|
|
15
|
+
}),
|
|
16
|
+
app: Args.string({
|
|
17
|
+
description: "Office app for sideload (excel | powerpoint | word)",
|
|
18
|
+
required: false,
|
|
19
|
+
options: OFFICE_APPS,
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
static flags = {
|
|
24
|
+
out: Flags.string({ char: "o", description: "Write manifest.json to this path (manifest action)" }),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
async run(): Promise<void> {
|
|
28
|
+
const { args, flags } = await this.parse(Office);
|
|
29
|
+
if (!args.action) {
|
|
30
|
+
console.log(`Usage: xcsh office <${OFFICE_ACTIONS.join("|")}>`);
|
|
31
|
+
console.log(" serve Start the https://127-0-0-1.local-ip.sh:8444 task-pane listener");
|
|
32
|
+
console.log(" manifest Print (or -o write) the add-in manifest.json");
|
|
33
|
+
console.log(" sideload Sideload the add-in into a desktop Office app");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
await runOfficeCommand({
|
|
37
|
+
action: args.action as OfficeAction,
|
|
38
|
+
app: args.app as OfficeApp | undefined,
|
|
39
|
+
out: flags.out,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.66.1",
|
|
21
|
+
"commit": "818d8885828e0e5fb5a9c23d5317d2584ed1a33e",
|
|
22
|
+
"shortCommit": "818d888",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-07-
|
|
26
|
-
"buildDate": "2026-07-
|
|
24
|
+
"tag": "v19.66.1",
|
|
25
|
+
"commitDate": "2026-07-20T22:06:59Z",
|
|
26
|
+
"buildDate": "2026-07-20T22:36:00.677Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/818d8885828e0e5fb5a9c23d5317d2584ed1a33e",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.66.1"
|
|
33
33
|
};
|